Diff
Modified: trunk/Websites/webkit.org/ChangeLog (201612 => 201613)
--- trunk/Websites/webkit.org/ChangeLog 2016-06-02 19:57:41 UTC (rev 201612)
+++ trunk/Websites/webkit.org/ChangeLog 2016-06-02 20:05:08 UTC (rev 201613)
@@ -1,3 +1,24 @@
+2016-06-02 Filip Pizlo <[email protected]>
+
+ Fix typos and make some revisions to the B3 docs
+ https://bugs.webkit.org/show_bug.cgi?id=158311
+
+ Reviewed by Michael Saboff.
+
+ I found typos and fixed them. Also, I clarified some things:
+
+ - Is B3 IR platform-agnostic? Sort of. I tried to describe when it is (Values usually behave
+ the same way regardless of CPU) and when it isn't (it lets you speak of registers if that's
+ what you want to do, for example).
+
+ - How does isValidForm really get used? You don't really need to create an Inst to use it.
+
+ - Some other incremental improvements to make the docs clearer.
+
+ * docs/b3/assembly-intermediate-representation.html:
+ * docs/b3/index.html:
+ * docs/b3/intermediate-representation.html:
+
2016-05-31 Filip Pizlo <[email protected]>
Unreviewed, fix an obvious typo: a missing comma.
Modified: trunk/Websites/webkit.org/docs/b3/assembly-intermediate-representation.html (201612 => 201613)
--- trunk/Websites/webkit.org/docs/b3/assembly-intermediate-representation.html 2016-06-02 19:57:41 UTC (rev 201612)
+++ trunk/Websites/webkit.org/docs/b3/assembly-intermediate-representation.html 2016-06-02 20:05:08 UTC (rev 201613)
@@ -44,13 +44,12 @@
<p>B3 is designed to be portable to many kinds of CPUs. Currently, it supports x86-64 and ARM64,
which are quite different from each other. In B3 IR, we expose very few instruction set
- details. Most clients only have to worry about the pointer type varying between Int32 and
- Int64. It's a goal of B3 IR to ensure that B3 values behave the same way except when the
- alternative would be prohibitive (like with pointer size or the corner-case behaviors of
- division). But to effectively compile code to different CPUs, the compiler has to eventually
- make instruction set details explicit. This is where Air comes in. B3 locks in most
- CPU-specific details at the moment of conversion to Air, and the Air code is irreversibly tied
- to some specific CPU.</p>
+ details. It's a goal of B3 IR to ensure that B3 values behave the same way except when the
+ alternative would be counterproductive (like with pointer size, the corner-case behaviors of
+ division, or calling convention customization). But to effectively compile code to different
+ CPUs, the compiler has to eventually make instruction set details explicit. This is where Air
+ comes in. B3 locks in most CPU-specific details at the moment of conversion to Air, and the Air
+ code is irreversibly tied to some specific CPU.</p>
<p>Air is an instruction <i>superset</i>: it recognizes all of the instructions from all CPUs
that Air may target. In its lowest-level form, Air is simply a way of describing an assembly
@@ -60,26 +59,33 @@
<a href=""
and abstract
<a href=""
- slots</a>.</p>
+ slots</a>. A <code>Tmp</code> object can either hold an unallocated temporary or a
+ register.</p>
<h3>Air as an Instruction Superset</h3>
- <p>It is possible to speak of an x86-64 instruction while compiling for ARM64,
- for example. Clients of Air, such as the B3 to Air lowering phase, are allowed to pick with any
- Air opcode and ask if that opcode would be valid on the current CPU. They are also allowed to
- check if specific forms of any given opcode are valid. This allows clients to optimize for
- multiple instruction sets by cascading through the possible opcodes that they know of, starting
- with the one they think is most efficient. Some of those opcodes may only be available on one
- CPU while others are available everywhere.</p>
+ <p>Air has syntax to speak of all of the CPU instructions we know about. It is possible to speak
+ of an x86-64 instruction while compiling for ARM64, for example. Clients of Air, such as the B3
+ to Air lowering phase, are allowed to pick any Air opcode and ask if that opcode would be
+ valid on the current CPU. They are also allowed to check if specific forms of any given opcode
+ are valid. This allows clients to optimize for multiple instruction sets by cascading through
+ the possible opcodes that they know of, starting with the one they think is most efficient.
+ Some of those opcodes may only be available on one CPU while others are available
+ everywhere. Instruction selection does not need to know which instructions work on which CPUs;
+ Air will tell you if some instruction happens to not be valid right now for whatever reason.</p>
<p>Air opcodes support overloading. For example, the Add32 opcode has both two-operand and
- three-operand overloads, and those overloads have multiple forms: the first operand may or
- may not be an immediate and depending on the CPU, some of the other operands may or may not
- be memory addresses. A fundamental Air operation is <code>Inst::isValidForm()</code>, which
- tells the client if the instruction's current form is valid on the current CPU. This may
- return false either because the Inst is not well-formed for any CPU or because it is not
- valid for the current CPU even though it may be valid on some other CPU. This allows clients
- to generate Air by experimenting with various different instruction forms before settling on
- the one that the current CPU supports.</p>
+ three-operand <i>overloads</i>, and those overloads have multiple <i>forms</i>: the first
+ operand may or may not be permitted to be an immediate and depending on the CPU and some of the
+ other operands may or may not be allowed to be memory addresses. We use <i>opcode overload</i>
+ to refer to all forms of an opcode that share the same number of arguments, and <i>opcode
+ form</i> to mean the number of arguments and their types. A fundamental Air operation is
+ <code>Inst::isValidForm()</code>, which tells the client if the instruction's current form is
+ valid on the current CPU. This may return false either because the Inst is not well-formed for
+ any CPU or because it is not valid for the current CPU even though it may be valid on some
+ other CPU. There is also <code>Air::isValidForm()</code>, which can answer if the form you are
+ intending to use will be valid even if you have not created an <code>Inst</code> yet. This
+ allows clients to generate Air by experimenting with different forms before settling on the one
+ that the current CPU supports.</p>
<h3>Air as a High-Level Assembly</h3>
<p>Air doesn't require the client to perform register or stack allocation. Anywhere that Air
@@ -93,12 +99,12 @@
registers, so it's always possible to determine which registers are live at any point in the
Air code.</p>
- <p>Air's philosophy allows B3 to use it for converting high-level, CPU-agnostic SSA procedures
- into code for the current CPU. Air is an instruction superset that allows clients to consider
- all available instructions on all possible CPUs and query which forms of those instructions are
- available on the current CPU. Air also supports for high-level concepts like <code>Tmp</code>s
- and stack slots, which allows B3 to Air lowering to focus on which instructions to use without
- worrying about register allocation or stack layout.</p>
+ <p>Air's philosophy allows B3 to use it for converting high-level, mostly-CPU-agnostic SSA
+ procedures into code for the current CPU. Air is an instruction superset that allows clients to
+ consider all available instructions on all possible CPUs and query which forms of those
+ instructions are available on the current CPU. Air also supports for high-level concepts like
+ <code>Tmp</code>s and stack slots, which allows B3 to Air lowering to focus on which
+ instructions to use without worrying about register allocation or stack layout.</p>
<h2>Args and the Air Execution Model</h2>
<p>Air can be thought of as an
@@ -107,7 +113,7 @@
arguments. The opcode determines what Air will do to the arguments - it may read from them or
write to them, for example. Orthognality implies that any argument that is read may be either
a register (or <code>Tmp</code>), an address, or an immediate; while any argument that is
- written may be either a register or an address. Air constraints orthognality where the target
+ written may be either a register or an address. Air constrains orthognality where the target
CPU would. For example, none of Air's target CPUs would support an <code>Add32</code>
instruction that loads its sources from memory <i>and</i> stores its result into memory. Even
x86 doesn't go that far. Either before or after creating an <code>Inst</code>, the client can
@@ -186,9 +192,9 @@
<li>Perform <i>late</i> actions.</li>
</ol>
- <p>Note that the early actions of one instruction happen immediately after the late actions of
+ <p>The early actions of one instruction happen immediately after the late actions of
the instruction before it. However, many Air analyses view them as happening at the same time.
- For example, any register usage in the early action of one instruction interfere with the
+ For example, any register usage in the early action of one instruction interferes with the
register usage in the late action of the instruction that came before it. All of Air's
liveness and interference analyses reason about the
<a href="" posts</i></a>
@@ -245,11 +251,17 @@
<li><code>func(%rcx, UseZDef, GP, Width32)</code></li>
</ol>
+ <p>Air's introspection of <code>Inst</code>s tends to be quite fast thanks to the use of template
+ specialization and C++ lambdas. The <code>forEachArg()</code> template method uses an efficient
+ arrangement of switch statements to determine the opcode and overload. If <code>func</code> is
+ a C++ lambda, we expect <code>forEachArg()</code> to be specialized for that lambda. Therefore,
+ this idiom avoids virtual dispatch or memory allocation.</p>
+
<p>Air supports exotic roles, such as late uses and early defs. There is even the
<code>Scratch</code> role, which means early def and late use. Speaking of a <code>Tmp</code>
in the <code>Scratch</code> role means that the <code>Tmp</code> will be assigned a register
that is guaranteed to not interfere with any of the other registers that the instruction
- speaks of. Late uses and early defs are crucial for patchpoints, which may for example require
+ speaks of. Late uses and early defs are crucial for patchpoints, which may require
that one of the incoming values be given a register that does not interfere with whatever
register is used for the result. This can be expressed either as giving the inputs a late use
role or by giving the outputs an early def role. The full list of possible roles is:</p>
@@ -299,7 +311,7 @@
instruction have to do with arguments, and what happens to each argument during the early and
late actions is determined by the opcode and the number of arguments (i.e. the overload).
Clients of Air may create an <code>Inst</code> with any combination of opcode and arguments
- and then query, using <code>Inst::isValidForm()</code> if the opcode, overload, and specific
+ and then query, using <code>isValidForm()</code> if the opcode, overload, and specific
arguments are valid for the current CPU.</p>
<h2>Defining Air</h2>
@@ -344,6 +356,12 @@
<p>Prefixing any line with <code>x86:</code> means that this form is only available on x86
CPUs, such as x86 or x86-64.</p>
+ <p>Air opcodes are designed to work with _javascript_Core's existing MacroAssembler. By default, an
+ opcode is automatically given a code generator that calls
+ <code>MacroAssembler::<i>opcodeName</i></code>, where <i>opcodeName</i> is derived by
+ lower-casing the first letter of the Air opcode name. <code>Add32</code> becomes
+ <code>MacroAssembler::add32</code>, for example.</p>
+
<p>See the header of
<a href=""
for a complete list of shorthand used by Air's opcode definition language.</p>
Modified: trunk/Websites/webkit.org/docs/b3/index.html (201612 => 201613)
--- trunk/Websites/webkit.org/docs/b3/index.html 2016-06-02 19:57:41 UTC (rev 201612)
+++ trunk/Websites/webkit.org/docs/b3/index.html 2016-06-02 20:05:08 UTC (rev 201613)
@@ -30,7 +30,8 @@
<p>Here's a simple example of C++ code that uses B3 to generate a function that adds two to
its argument and returns it:</p>
- <pre><code>Procedure proc;
+ <pre><code>// Create a Procedure that holds our code.
+Procedure proc;
BasicBlock* root = proc.addBlock();
root->appendNew<ControlValue>(
proc, Return, Origin(),
@@ -39,10 +40,15 @@
root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
root->appendNew<Const64Value>(proc, Origin(), 2)));
+// Have B3 compile the Procedure into code. The code and all of its artifacts (constant pools, jump tables)
+// will stay alive so long as Compilation stays alive.
std::unique_ptr<Compilation> compilation = std::make_unique<Compilation>(vm, proc);
+
+// Get a function pointer that we can call. This function pointer points to JIT-generated machine code.
int64_t (*function)(int64_t) = bitwise_cast<int64_t (*)(int64_t)>(compilation->code().executableAddress());
-printf("%lld\n", function(42)); // prints 44</code></pre>
+// Run it and print the result!
+printf("%lld\n", function(42)); // Prints 44.</code></pre>
<p>When compiled, the resulting machine code looks like this:</p>
@@ -62,17 +68,18 @@
<p>Clients of B3 usually interact with it using
<a href="" IR</a>. It's C-like, in the sense that it
models heap references as integers and does not attempt to verify memory accesses. It
- enforces static single assignment, or SSA for short. An SSA program will contain only one
- assignment to each variable, which makes it trivial to trace from a use of a variable to
- the operation that defined its value. B3 IR is designed to be easy to generate and cheap
- to manipulate.</p>
+ enforces <a href="" single
+ assignment</a>, or SSA for short. An SSA program will contain only one assignment to each
+ variable, which makes it trivial to trace from a use of a variable to the operation that
+ defined its value. B3 IR is designed to be easy to generate and cheap to manipulate.</p>
- <p>B3 is designed to be used as a backend for JITs, rather than as a tool that programmers
- use directly. Therefore, B3 embraces platform-specific concepts like argument registers,
- stack frame layout, the frame pointer, and the call argument areas. It's possible to emit
- B3 IR that defines completely novel calling conventions, both for callers of the procedure
- being generated and for callees of the procedure's callsites. B3 also makes it easy to
- just emit a C call. There's an opcode for that.</p>
+ <p>In most ways, B3 IR is platform-agnostic. However, since B3 is designed to be used as a
+ backend for JITs, it does embrace some platform-specific concepts whenever it is pragmatic to
+ do so. B3 exposes direct control over argument registers, stack frame layout, the frame
+ pointer, and the call argument areas. It's possible to emit B3 IR that defines completely
+ novel calling conventions, both for callers of the procedure being generated and for callees of
+ the procedure's callsites. B3 also makes it easy to just emit a C call. There's an opcode for
+ that.</p>
<p>See <a href="" IR documentation</a> for more
info.</p>
@@ -146,7 +153,7 @@
Add64 $2, %rdi, %rax, @2
Ret64 %rax, @3</code></pre>
- <h2>B3->Air lowering, also known as Instruction Selection</h2>
+ <h2>B3→Air lowering, also known as Instruction Selection</h2>
<p>The B3::LowerToAir phase converts B3 into Air by doing pattern-matching. It processes
programs backwards. At each B3 value, it greedily tries to match both the value and as
@@ -193,8 +200,8 @@
<h2>Code generation</h2>
- <p>The final form of Air contains no registers or abstract stack slots. Therefore, it maps
- directly to machine code. The final code generation step is a very fast transformation
+ <p>The final form of Air contains no unallocated temporaries or abstract stack slots. Therefore,
+ it maps directly to machine code. The final code generation step is a very fast transformation
from Air's object-oriented way of representing those instructions to the target's machine
code. We use _javascript_Core's macro assembler for this purpose.</p>
Modified: trunk/Websites/webkit.org/docs/b3/intermediate-representation.html (201612 => 201613)
--- trunk/Websites/webkit.org/docs/b3/intermediate-representation.html 2016-06-02 19:57:41 UTC (rev 201612)
+++ trunk/Websites/webkit.org/docs/b3/intermediate-representation.html 2016-06-02 20:05:08 UTC (rev 201613)
@@ -65,6 +65,16 @@
<dd>64-bit binary floating point number.</dd>
</dl>
+ <p>B3 does not have a pointer type. Instead, the <code>B3::pointerType()</code> function will
+ return either Int32 or Int64 depending on which kind of integer can be used to represent a
+ pointer on the current platform. It's not a goal of B3 to support hardware targets that require
+ pointers and integers to be segregated. It's not a goal of B3 to support GC (garbage
+ collection) roots as a separate type, since JSC uses
+ <a href="" conservative
+ root scanning</a>. This doesn't preclude any mainstream garbage collection algorithms,
+ including copying, generational, or concurrent collectors, and frees up the compiler to perform
+ more optimizations.</p>
+
<h2>Values</h2>
<p>Variables, and the instructions that define them, are represented using the Value object.
@@ -412,11 +422,12 @@
the CCallValue class.</dd>
<dt>T1 Patchpoint([T2, [T3, ...]])</dt>
- <dd>A Patchpoint is a customizable value. Patchpoints take zero or more values of any
- type and return any type. A Patchpoint's behavior is determined by the generator
- object. The generator is a C++ lambda that gets called during code generation. It gets
- passed an assembler instance (specifically, CCallHelpers&) and an object describing
- where to find all of the input values and where to put the result. Here's an example:
+ <dd>
+ <p>A Patchpoint is a customizable value. Patchpoints take zero or more values of any
+ type and return any type. A Patchpoint's behavior is determined by the generator
+ object. The generator is a C++ lambda that gets called during code generation. It gets
+ passed an assembler instance (specifically, CCallHelpers&) and an object describing
+ where to find all of the input values and where to put the result. Here's an example:</p>
<pre><code>PatchpointValue* patchpoint = block->appendNew<PatchpointValue>(proc, Int32, Origin());
patchpoint->append(ConstrainedValue(arg1, ValueRep::SomeRegister));