<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[193386] trunk/Source/JavaScriptCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/193386">193386</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-12-03 16:11:32 -0800 (Thu, 03 Dec 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>B3 patchpoints should allow specifying output constraints
https://bugs.webkit.org/show_bug.cgi?id=151809
Reviewed by Benjamin Poulain.
JS call patchpoints should put their result into the result register, while most other patchpoints
should put their results into some register. I think that it's best if we just allow arbitrary
constraints on the result of a patchpoint. And by "arbitrary" I mean allowing the same kinds of
constraints as we allow on the stackmap children.
This also adds a large comment in B3StackmapValue.h that lays out the philosophy of our stackmaps
and patchpoints. I found it useful to write down the plan since it's pretty subtle.
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::lower):
* b3/B3PatchpointSpecial.cpp:
(JSC::B3::PatchpointSpecial::isValid):
(JSC::B3::PatchpointSpecial::admitsStack):
* b3/B3PatchpointValue.cpp:
(JSC::B3::PatchpointValue::~PatchpointValue):
(JSC::B3::PatchpointValue::dumpMeta):
(JSC::B3::PatchpointValue::PatchpointValue):
* b3/B3PatchpointValue.h:
(JSC::B3::PatchpointValue::accepts):
* b3/B3Procedure.h:
(JSC::B3::Procedure::code):
* b3/B3StackmapSpecial.cpp:
(JSC::B3::StackmapSpecial::isValidImpl):
(JSC::B3::StackmapSpecial::appendRepsImpl):
(JSC::B3::StackmapSpecial::isArgValidForValue):
(JSC::B3::StackmapSpecial::isArgValidForRep):
(JSC::B3::StackmapSpecial::repForArg):
* b3/B3StackmapSpecial.h:
* b3/B3StackmapValue.h:
* b3/B3Validate.cpp:
* b3/B3ValueRep.h:
(JSC::B3::ValueRep::doubleValue):
* b3/testb3.cpp:
(JSC::B3::testPatchpointManyImms):
(JSC::B3::testPatchpointWithRegisterResult):
(JSC::B3::testPatchpointWithStackArgumentResult):
(JSC::B3::testPatchpointWithAnyResult):
(JSC::B3::testSimpleCheck):
(JSC::B3::run):
* jit/RegisterSet.h:</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3LowerToAircpp">trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3PatchpointSpecialcpp">trunk/Source/JavaScriptCore/b3/B3PatchpointSpecial.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3PatchpointValuecpp">trunk/Source/JavaScriptCore/b3/B3PatchpointValue.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3PatchpointValueh">trunk/Source/JavaScriptCore/b3/B3PatchpointValue.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Procedureh">trunk/Source/JavaScriptCore/b3/B3Procedure.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3StackmapSpecialcpp">trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3StackmapSpecialh">trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3StackmapValueh">trunk/Source/JavaScriptCore/b3/B3StackmapValue.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Validatecpp">trunk/Source/JavaScriptCore/b3/B3Validate.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3ValueReph">trunk/Source/JavaScriptCore/b3/B3ValueRep.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3testb3cpp">trunk/Source/JavaScriptCore/b3/testb3.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitRegisterSeth">trunk/Source/JavaScriptCore/jit/RegisterSet.h</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -1,3 +1,51 @@
</span><ins>+2015-12-03 Filip Pizlo <fpizlo@apple.com>
+
+ B3 patchpoints should allow specifying output constraints
+ https://bugs.webkit.org/show_bug.cgi?id=151809
+
+ Reviewed by Benjamin Poulain.
+
+ JS call patchpoints should put their result into the result register, while most other patchpoints
+ should put their results into some register. I think that it's best if we just allow arbitrary
+ constraints on the result of a patchpoint. And by "arbitrary" I mean allowing the same kinds of
+ constraints as we allow on the stackmap children.
+
+ This also adds a large comment in B3StackmapValue.h that lays out the philosophy of our stackmaps
+ and patchpoints. I found it useful to write down the plan since it's pretty subtle.
+
+ * b3/B3LowerToAir.cpp:
+ (JSC::B3::Air::LowerToAir::lower):
+ * b3/B3PatchpointSpecial.cpp:
+ (JSC::B3::PatchpointSpecial::isValid):
+ (JSC::B3::PatchpointSpecial::admitsStack):
+ * b3/B3PatchpointValue.cpp:
+ (JSC::B3::PatchpointValue::~PatchpointValue):
+ (JSC::B3::PatchpointValue::dumpMeta):
+ (JSC::B3::PatchpointValue::PatchpointValue):
+ * b3/B3PatchpointValue.h:
+ (JSC::B3::PatchpointValue::accepts):
+ * b3/B3Procedure.h:
+ (JSC::B3::Procedure::code):
+ * b3/B3StackmapSpecial.cpp:
+ (JSC::B3::StackmapSpecial::isValidImpl):
+ (JSC::B3::StackmapSpecial::appendRepsImpl):
+ (JSC::B3::StackmapSpecial::isArgValidForValue):
+ (JSC::B3::StackmapSpecial::isArgValidForRep):
+ (JSC::B3::StackmapSpecial::repForArg):
+ * b3/B3StackmapSpecial.h:
+ * b3/B3StackmapValue.h:
+ * b3/B3Validate.cpp:
+ * b3/B3ValueRep.h:
+ (JSC::B3::ValueRep::doubleValue):
+ * b3/testb3.cpp:
+ (JSC::B3::testPatchpointManyImms):
+ (JSC::B3::testPatchpointWithRegisterResult):
+ (JSC::B3::testPatchpointWithStackArgumentResult):
+ (JSC::B3::testPatchpointWithAnyResult):
+ (JSC::B3::testSimpleCheck):
+ (JSC::B3::run):
+ * jit/RegisterSet.h:
+
</ins><span class="cx"> 2015-12-03 Anders Carlsson <andersca@apple.com>
</span><span class="cx">
</span><span class="cx"> Remove Objective-C GC support
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3LowerToAircpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -1696,13 +1696,38 @@
</span><span class="cx"> ensureSpecial(m_patchpointSpecial);
</span><span class="cx">
</span><span class="cx"> Inst inst(Patch, patchpointValue, Arg::special(m_patchpointSpecial));
</span><ins>+
+ Vector<Inst> after;
+ if (patchpointValue->type() != Void) {
+ switch (patchpointValue->resultConstraint.kind()) {
+ case ValueRep::Any:
+ case ValueRep::SomeRegister:
+ inst.args.append(tmp(patchpointValue));
+ break;
+ case ValueRep::Register: {
+ Tmp reg = Tmp(patchpointValue->resultConstraint.reg());
+ inst.args.append(reg);
+ after.append(Inst(
+ relaxedMoveForType(patchpointValue->type()), m_value, reg, tmp(patchpointValue)));
+ break;
+ }
+ case ValueRep::StackArgument: {
+ Arg arg = Arg::callArg(patchpointValue->resultConstraint.offsetFromSP());
+ inst.args.append(arg);
+ after.append(Inst(
+ moveForType(patchpointValue->type()), m_value, arg, tmp(patchpointValue)));
+ break;
+ }
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+ }
</ins><span class="cx">
</span><del>- if (patchpointValue->type() != Void)
- inst.args.append(tmp(patchpointValue));
-
</del><span class="cx"> fillStackmap(inst, patchpointValue, 0);
</span><span class="cx">
</span><span class="cx"> m_insts.last().append(WTF::move(inst));
</span><ins>+ m_insts.last().appendVector(after);
</ins><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3PatchpointSpecialcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3PatchpointSpecial.cpp (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3PatchpointSpecial.cpp        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3PatchpointSpecial.cpp        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -64,9 +64,10 @@
</span><span class="cx">
</span><span class="cx"> if (inst.args.size() < 2)
</span><span class="cx"> return false;
</span><del>- if (inst.args[1].kind() != Arg::Tmp)
</del><ins>+ PatchpointValue* patchpoint = inst.origin->as<PatchpointValue>();
+ if (!isArgValidForValue(inst.args[1], patchpoint))
</ins><span class="cx"> return false;
</span><del>- if (!inst.args[1].isType(inst.origin->airType()))
</del><ins>+ if (!isArgValidForRep(code(), inst.args[1], patchpoint->resultConstraint))
</ins><span class="cx"> return false;
</span><span class="cx">
</span><span class="cx"> return isValidImpl(0, 2, inst);
</span><span class="lines">@@ -77,8 +78,19 @@
</span><span class="cx"> if (inst.origin->type() == Void)
</span><span class="cx"> return admitsStackImpl(0, 1, inst, argIndex);
</span><span class="cx">
</span><del>- if (argIndex == 1)
- return false;
</del><ins>+ if (argIndex == 1) {
+ switch (inst.origin->as<PatchpointValue>()->resultConstraint.kind()) {
+ case ValueRep::Any:
+ case ValueRep::StackArgument:
+ return true;
+ case ValueRep::SomeRegister:
+ case ValueRep::Register:
+ return false;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
</ins><span class="cx">
</span><span class="cx"> return admitsStackImpl(0, 2, inst, argIndex);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3PatchpointValuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3PatchpointValue.cpp (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3PatchpointValue.cpp        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3PatchpointValue.cpp        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -34,9 +34,16 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+void PatchpointValue::dumpMeta(CommaPrinter& comma, PrintStream& out) const
+{
+ Base::dumpMeta(comma, out);
+ out.print(comma, "resultConstraint = ", resultConstraint);
+}
+
</ins><span class="cx"> PatchpointValue::PatchpointValue(unsigned index, Type type, Origin origin)
</span><del>- : StackmapValue(index, CheckedOpcode, Patchpoint, type, origin)
</del><ins>+ : Base(index, CheckedOpcode, Patchpoint, type, origin)
</ins><span class="cx"> , effects(Effects::forCall())
</span><ins>+ , resultConstraint(type == Void ? ValueRep::Any : ValueRep::SomeRegister)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3PatchpointValueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3PatchpointValue.h (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3PatchpointValue.h        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3PatchpointValue.h        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -36,12 +36,29 @@
</span><span class="cx">
</span><span class="cx"> class PatchpointValue : public StackmapValue {
</span><span class="cx"> public:
</span><ins>+ typedef StackmapValue Base;
+
</ins><span class="cx"> static bool accepts(Opcode opcode) { return opcode == Patchpoint; }
</span><span class="cx">
</span><span class="cx"> ~PatchpointValue();
</span><span class="cx">
</span><ins>+ // The effects of the patchpoint. This defaults to Effects::forCall(), but you can set it to anything.
+ //
+ // If there are no effects, B3 is free to assume any use of this PatchpointValue can be replaced with
+ // a use of a different PatchpointValue, so long as the other one also has no effects and has the
+ // same children. Note that this comparison ignores child constraints, the result constraint, and all
+ // other StackmapValue meta-data. If there are read effects but not write effects, then this same sort
+ // of substitution could be made so long as there are no interfering writes.
</ins><span class="cx"> Effects effects;
</span><span class="cx">
</span><ins>+ // The input representation (i.e. constraint) of the return value. This defaults to Any if the type is
+ // Void and it defaults to SomeRegister otherwise. It's illegal to mess with this if the type is Void.
+ // Otherwise you can set this to any input constraint.
+ ValueRep resultConstraint;
+
+protected:
+ void dumpMeta(CommaPrinter&, PrintStream&) const override;
+
</ins><span class="cx"> private:
</span><span class="cx"> friend class Procedure;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Procedureh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Procedure.h (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Procedure.h        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3Procedure.h        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -224,7 +224,7 @@
</span><span class="cx"> const Air::Code& code() const { return *m_code; }
</span><span class="cx"> Air::Code& code() { return *m_code; }
</span><span class="cx">
</span><del>- unsigned frameSize() const;
</del><ins>+ JS_EXPORT_PRIVATE unsigned frameSize() const;
</ins><span class="cx"> const RegisterAtOffsetList& calleeSaveRegisters() const;
</span><span class="cx">
</span><span class="cx"> private:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3StackmapSpecialcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -118,26 +118,8 @@
</span><span class="cx"> for (unsigned i = 0; i < inst.args.size() - numIgnoredAirArgs; ++i) {
</span><span class="cx"> Value* child = value->child(i + numIgnoredB3Args);
</span><span class="cx"> Arg& arg = inst.args[i + numIgnoredAirArgs];
</span><del>-
- switch (arg.kind()) {
- case Arg::Tmp:
- case Arg::Imm:
- case Arg::Imm64:
- case Arg::Stack:
- case Arg::CallArg:
- break; // OK
- case Arg::Addr:
- if (arg.base() != Tmp(GPRInfo::callFrameRegister)
- && arg.base() != Tmp(MacroAssembler::stackPointerRegister))
- return false;
- break;
- default:
- return false;
- }
-
- Arg::Type type = Arg::typeForB3Type(child->type());
</del><span class="cx">
</span><del>- if (!arg.isType(type))
</del><ins>+ if (!isArgValidForValue(arg, child))
</ins><span class="cx"> return false;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -149,39 +131,8 @@
</span><span class="cx"> ValueRep& rep = value->m_reps[i];
</span><span class="cx"> Arg& arg = inst.args[i - numIgnoredB3Args + numIgnoredAirArgs];
</span><span class="cx">
</span><del>- switch (rep.kind()) {
- case ValueRep::Any:
- // We already verified this above.
- break;
- case ValueRep::SomeRegister:
- if (!arg.isTmp())
- return false;
- break;
- case ValueRep::Register:
- if (arg != Tmp(rep.reg()))
- return false;
- break;
- case ValueRep::Stack:
- // This is not a valid input representation.
- ASSERT_NOT_REACHED();
- break;
- case ValueRep::StackArgument:
- if (arg == Arg::callArg(rep.offsetFromSP()))
- break;
- if (arg.isAddr() && code().frameSize()) {
- if (arg.base() == Tmp(GPRInfo::callFrameRegister)
- && arg.offset() == rep.offsetFromSP() - code().frameSize())
- break;
- if (arg.base() == Tmp(MacroAssembler::stackPointerRegister)
- && arg.offset() == rep.offsetFromSP())
- break;
- }
</del><ins>+ if (!isArgValidForRep(code(), arg, rep))
</ins><span class="cx"> return false;
</span><del>- case ValueRep::Constant:
- // This is not a valid input representation.
- ASSERT_NOT_REACHED();
- break;
- }
</del><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> return true;
</span><span class="lines">@@ -216,6 +167,57 @@
</span><span class="cx"> result.append(repForArg(*context.code, inst.args[i]));
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+bool StackmapSpecial::isArgValidForValue(const Arg::Arg& arg, Value* value)
+{
+ switch (arg.kind()) {
+ case Arg::Tmp:
+ case Arg::Imm:
+ case Arg::Imm64:
+ case Arg::Stack:
+ case Arg::CallArg:
+ break; // OK
+ case Arg::Addr:
+ if (arg.base() != Tmp(GPRInfo::callFrameRegister)
+ && arg.base() != Tmp(MacroAssembler::stackPointerRegister))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ Arg::Type type = Arg::typeForB3Type(value->type());
+
+ return arg.isType(type);
+}
+
+bool StackmapSpecial::isArgValidForRep(Air::Code& code, const Air::Arg& arg, const ValueRep& rep)
+{
+ switch (rep.kind()) {
+ case ValueRep::Any:
+ // We already verified by isArgValidForValue().
+ return true;
+ case ValueRep::SomeRegister:
+ return arg.isTmp();
+ case ValueRep::Register:
+ return arg == Tmp(rep.reg());
+ case ValueRep::StackArgument:
+ if (arg == Arg::callArg(rep.offsetFromSP()))
+ return true;
+ if (arg.isAddr() && code.frameSize()) {
+ if (arg.base() == Tmp(GPRInfo::callFrameRegister)
+ && arg.offset() == rep.offsetFromSP() - code.frameSize())
+ return true;
+ if (arg.base() == Tmp(MacroAssembler::stackPointerRegister)
+ && arg.offset() == rep.offsetFromSP())
+ return true;
+ }
+ return false;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
</ins><span class="cx"> ValueRep StackmapSpecial::repForArg(Code& code, const Arg& arg)
</span><span class="cx"> {
</span><span class="cx"> switch (arg.kind()) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3StackmapSpecialh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -66,6 +66,8 @@
</span><span class="cx"> // Appends the reps for the Inst's args, starting with numIgnoredArgs, to the given vector.
</span><span class="cx"> void appendRepsImpl(Air::GenerationContext&, unsigned numIgnoredArgs, Air::Inst&, Vector<ValueRep>&);
</span><span class="cx">
</span><ins>+ static bool isArgValidForValue(const Air::Arg&, Value*);
+ static bool isArgValidForRep(Air::Code&, const Air::Arg&, const ValueRep&);
</ins><span class="cx"> static ValueRep repForArg(Air::Code&, const Air::Arg&);
</span><span class="cx"> };
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3StackmapValueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3StackmapValue.h (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3StackmapValue.h        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3StackmapValue.h        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -97,6 +97,79 @@
</span><span class="cx">
</span><span class="cx"> const Vector<ValueRep>& reps() const { return m_reps; }
</span><span class="cx">
</span><ins>+ // Stackmaps allow you to specify that the operation may clobber some registers. Clobbering a register
+ // means that the operation appears to store a value into the register, but the compiler doesn't
+ // assume to know anything about what kind of value might have been stored. In B3's model of
+ // execution, registers are read or written at instruction boundaries rather than inside the
+ // instructions themselves. A register could be read or written immediately before the instruction
+ // executes, or immediately after. Note that at a boundary between instruction A and instruction B we
+ // simultaneously look at what A does after it executes and what B does before it executes. This is
+ // because when the compiler considers what happens to registers, it views the boundary between two
+ // instructions as a kind of atomic point where the late effects of A happen at the same time as the
+ // early effects of B.
+ //
+ // The compiler views a stackmap as a single instruction, even though of course the stackmap may be
+ // composed of any number of instructions (if it's a Patchpoint). You can claim that a stackmap value
+ // clobbers a set of registers before the stackmap's instruction or after. Clobbering before is called
+ // early clobber, while clobbering after is called late clobber.
+ //
+ // This is quite flexible but it has its limitations. Any register listed as an early clobber will
+ // interfere with all uses of the stackmap. Any register listed as a late clobber will interfere with
+ // all defs of the stackmap (i.e. the result). This means that it's currently not possible to claim
+ // to clobber a register while still allowing that register to be used for both an input and an output
+ // of the instruction. It just so happens that B3's sole client (the FTL) currently never wants to
+ // convey such a constraint, but it will want it eventually (FIXME:
+ // https://bugs.webkit.org/show_bug.cgi?id=151823).
+ //
+ // Note that a common use case of early clobber sets is to indicate that this is the set of registers
+ // that shall not be used for inputs to the value. But B3 supports two different ways of specifying
+ // this, the other being LateUse in combination with late clobber (not yet available to stackmaps
+ // directly, FIXME: https://bugs.webkit.org/show_bug.cgi?id=151335). A late use makes the use of that
+ // value appear to happen after the instruction. This means that a late use cannot use the same
+ // register as the result and it cannot use the same register as either early or late clobbered
+ // registers. Late uses are usually a better way of saying that a clobbered register cannot be used
+ // for an input. Early clobber means that some register(s) interfere with *all* inputs, while LateUse
+ // means that some value interferes with whatever is live after the instruction. Below is a list of
+ // examples of how the FTL can handle its various kinds of scenarios using a combination of early
+ // clobber, late clobber, and late use. These examples are for X86_64, w.l.o.g.
+ //
+ // Basic ById patchpoint: Early and late clobber of r11. Early clobber prevents any inputs from using
+ // r11 since that would mess with the MacroAssembler's assumptions when we
+ // AllowMacroScratchRegisterUsage. Late clobber tells B3 that the patchpoint may overwrite r11.
+ //
+ // ById patchpoint in a try block with some live state: This might throw an exception after already
+ // assigning to the result. So, this should LateUse all stackmap values to ensure that the stackmap
+ // values don't interfere with the result. Note that we do not LateUse the non-OSR inputs of the ById
+ // since LateUse implies that the use is cold: the register allocator will assume that the use is not
+ // important for the critical path. Also, early and late clobber of r11.
+ //
+ // Basic ByIdFlush patchpoint: We could do Flush the same way we did it with LLVM: ignore it and let
+ // PolymorphicAccess figure it out. Or, we could add internal clobber support (FIXME:
+ // https://bugs.webkit.org/show_bug.cgi?id=151823). Or, we could do it by early clobbering r11, late
+ // clobbering all volatile registers, and constraining the result to some register. Or, we could do
+ // that but leave the result constrained to SomeRegister, which will cause it to use a callee-save
+ // register. Internal clobber support would allow us to use SomeRegister while getting the result into
+ // a volatile register.
+ //
+ // ByIdFlush patchpoint in a try block with some live state: LateUse all for-OSR stackmap values,
+ // early clobber of r11 to prevent the other inputs from using r11, and late clobber of all volatile
+ // registers to make way for the call. To handle the result, we could do any of what is listed in the
+ // previous paragraph.
+ //
+ // Basic JS call: Force all non-OSR inputs into specific locations (register, stack, whatever).
+ // All volatile registers are late-clobbered. The output is constrained to a register as well.
+ //
+ // JS call in a try block with some live state: LateUse all for-OSR stackmap values, fully constrain
+ // all non-OSR inputs and the result, and late clobber all volatile registers.
+ //
+ // JS tail call: Pass all inputs as a warm variant of Any (FIXME:
+ // https://bugs.webkit.org/show_bug.cgi?id=151811).
+ //
+ // Note that we cannot yet do all of these things because although Air already supports all of these
+ // various forms of uses (LateUse and warm unconstrained use), B3 doesn't yet expose all of it. The
+ // bugs are:
+ // https://bugs.webkit.org/show_bug.cgi?id=151335 (LateUse)
+ // https://bugs.webkit.org/show_bug.cgi?id=151811 (warm Any)
</ins><span class="cx"> void clobberEarly(const RegisterSet& set)
</span><span class="cx"> {
</span><span class="cx"> m_earlyClobbered.merge(set);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Validatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Validate.cpp (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Validate.cpp        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3Validate.cpp        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -290,6 +290,10 @@
</span><span class="cx"> // return type.
</span><span class="cx"> break;
</span><span class="cx"> case Patchpoint:
</span><ins>+ if (value->type() == Void)
+ VALIDATE(value->as<PatchpointValue>()->resultConstraint == ValueRep::Any, ("At ", *value));
+ else
+ validateStackmapConstraint(value, ConstrainedValue(value, value->as<PatchpointValue>()->resultConstraint));
</ins><span class="cx"> validateStackmap(value);
</span><span class="cx"> break;
</span><span class="cx"> case CheckAdd:
</span><span class="lines">@@ -338,14 +342,26 @@
</span><span class="cx"> StackmapValue* stackmap = value->as<StackmapValue>();
</span><span class="cx"> VALIDATE(stackmap, ("At ", *value));
</span><span class="cx"> VALIDATE(stackmap->numChildren() >= stackmap->reps().size(), ("At ", *stackmap));
</span><del>- for (unsigned i = 0; i < stackmap->reps().size(); ++i) {
- const ValueRep& rep = stackmap->reps()[i];
- if (rep.kind() != ValueRep::Register)
- continue;
- if (rep.reg().isGPR())
- VALIDATE(isInt(stackmap->child(i)->type()), ("At ", *stackmap));
</del><ins>+ for (ConstrainedValue child : stackmap->constrainedChildren())
+ validateStackmapConstraint(stackmap, child);
+ }
+
+ void validateStackmapConstraint(Value* context, const ConstrainedValue& value)
+ {
+ switch (value.rep().kind()) {
+ case ValueRep::Any:
+ case ValueRep::SomeRegister:
+ case ValueRep::StackArgument:
+ break;
+ case ValueRep::Register:
+ if (value.rep().reg().isGPR())
+ VALIDATE(isInt(value.value()->type()), ("At ", *context, ": ", value));
</ins><span class="cx"> else
</span><del>- VALIDATE(isFloat(stackmap->child(i)->type()), ("At ", *stackmap));
</del><ins>+ VALIDATE(isFloat(value.value()->type()), ("At ", *context, ": ", value));
+ break;
+ default:
+ VALIDATE(false, ("At ", *context, ": ", value));
+ break;
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3ValueReph"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3ValueRep.h (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3ValueRep.h        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/B3ValueRep.h        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -194,7 +194,7 @@
</span><span class="cx"> return bitwise_cast<double>(value());
</span><span class="cx"> }
</span><span class="cx">
</span><del>- void dump(PrintStream&) const;
</del><ins>+ JS_EXPORT_PRIVATE void dump(PrintStream&) const;
</ins><span class="cx">
</span><span class="cx"> private:
</span><span class="cx"> Kind m_kind;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3testb3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/testb3.cpp (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -3720,6 +3720,87 @@
</span><span class="cx"> CHECK(!compileAndRun<int>(proc));
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+void testPatchpointWithRegisterResult()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
+ patchpoint->append(ConstrainedValue(arg1, ValueRep::SomeRegister));
+ patchpoint->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
+ patchpoint->resultConstraint = ValueRep::reg(GPRInfo::nonArgGPR0);
+ patchpoint->setGenerator(
+ [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ CHECK(params.reps.size() == 3);
+ CHECK(params.reps[0] == ValueRep::reg(GPRInfo::nonArgGPR0));
+ CHECK(params.reps[1].isGPR());
+ CHECK(params.reps[2].isGPR());
+ jit.move(params.reps[1].gpr(), GPRInfo::nonArgGPR0);
+ jit.add32(params.reps[2].gpr(), GPRInfo::nonArgGPR0);
+ });
+ root->appendNew<ControlValue>(proc, Return, Origin(), patchpoint);
+
+ CHECK(compileAndRun<int>(proc, 1, 2) == 3);
+}
+
+void testPatchpointWithStackArgumentResult()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Int32, Origin());
+ patchpoint->append(ConstrainedValue(arg1, ValueRep::SomeRegister));
+ patchpoint->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
+ patchpoint->resultConstraint = ValueRep::stackArgument(0);
+ patchpoint->clobber(RegisterSet::macroScratchRegisters());
+ patchpoint->setGenerator(
+ [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ CHECK(params.reps.size() == 3);
+ CHECK(params.reps[0] == ValueRep::stack(-static_cast<intptr_t>(proc.frameSize())));
+ CHECK(params.reps[1].isGPR());
+ CHECK(params.reps[2].isGPR());
+ jit.store32(params.reps[1].gpr(), CCallHelpers::Address(CCallHelpers::stackPointerRegister, 0));
+ jit.add32(params.reps[2].gpr(), CCallHelpers::Address(CCallHelpers::stackPointerRegister, 0));
+ });
+ root->appendNew<ControlValue>(proc, Return, Origin(), patchpoint);
+
+ CHECK(compileAndRun<int>(proc, 1, 2) == 3);
+}
+
+void testPatchpointWithAnyResult()
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* arg1 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* arg2 = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1);
+ PatchpointValue* patchpoint = root->appendNew<PatchpointValue>(proc, Double, Origin());
+ patchpoint->append(ConstrainedValue(arg1, ValueRep::SomeRegister));
+ patchpoint->append(ConstrainedValue(arg2, ValueRep::SomeRegister));
+ patchpoint->resultConstraint = ValueRep::Any;
+ patchpoint->clobberLate(RegisterSet::allFPRs());
+ patchpoint->clobber(RegisterSet::macroScratchRegisters());
+ patchpoint->clobber(RegisterSet(GPRInfo::regT0));
+ patchpoint->setGenerator(
+ [&] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+ AllowMacroScratchRegisterUsage allowScratch(jit);
+ CHECK(params.reps.size() == 3);
+ CHECK(params.reps[0].isStack());
+ CHECK(params.reps[1].isGPR());
+ CHECK(params.reps[2].isGPR());
+ jit.move(params.reps[1].gpr(), GPRInfo::regT0);
+ jit.add32(params.reps[2].gpr(), GPRInfo::regT0);
+ jit.convertInt32ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0);
+ jit.storeDouble(FPRInfo::fpRegT0, CCallHelpers::Address(GPRInfo::callFrameRegister, params.reps[0].offsetFromFP()));
+ });
+ root->appendNew<ControlValue>(proc, Return, Origin(), patchpoint);
+
+ CHECK(compileAndRun<double>(proc, 1, 2) == 3);
+}
+
</ins><span class="cx"> void testSimpleCheck()
</span><span class="cx"> {
</span><span class="cx"> Procedure proc;
</span><span class="lines">@@ -5979,6 +6060,9 @@
</span><span class="cx"> RUN(testPatchpointAny());
</span><span class="cx"> RUN(testPatchpointAnyImm());
</span><span class="cx"> RUN(testPatchpointManyImms());
</span><ins>+ RUN(testPatchpointWithRegisterResult());
+ RUN(testPatchpointWithStackArgumentResult());
+ RUN(testPatchpointWithAnyResult());
</ins><span class="cx"> RUN(testSimpleCheck());
</span><span class="cx"> RUN(testCheckLessThan());
</span><span class="cx"> RUN(testCheckMegaCombo());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitRegisterSeth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/RegisterSet.h (193385 => 193386)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/RegisterSet.h        2015-12-03 23:59:31 UTC (rev 193385)
+++ trunk/Source/JavaScriptCore/jit/RegisterSet.h        2015-12-04 00:11:32 UTC (rev 193386)
</span><span class="lines">@@ -59,7 +59,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> static RegisterSet volatileRegistersForJSCall();
</span><span class="cx"> static RegisterSet stubUnavailableRegisters(); // The union of callee saves and special registers.
</span><del>- static RegisterSet macroScratchRegisters();
</del><ins>+ JS_EXPORT_PRIVATE static RegisterSet macroScratchRegisters();
</ins><span class="cx"> JS_EXPORT_PRIVATE static RegisterSet allGPRs();
</span><span class="cx"> JS_EXPORT_PRIVATE static RegisterSet allFPRs();
</span><span class="cx"> static RegisterSet allRegisters();
</span></span></pre>
</div>
</div>
</body>
</html>