Air should have a way of expressing additional instruction flags
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Sep 2016 16:59:24 +0000 (16:59 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Sep 2016 16:59:24 +0000 (16:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=162699

Reviewed by Mark Lam.
Source/JavaScriptCore:

This follows a similar change in B3 (r206595) and replaces Air::Opcode with Air::Kind,
which holds onto the opcode and some additional flags. Because Air is an orthogonal ISA
(the opcode tells you what the operation does but each operand is allowed to also contain
effectively instructions for what to do to read or write that operand), the flags are
meant to be orthogonal to opcode. This allows us to say things like Add32<Trap>, which
makes sense if any of the operands to the Add32 are addresses.

To demonstrate the flags facility this partly adds a trap flag to Air. B3 doesn't use it
yet, but I made sure that Air respects it. Basically that means blocking DCE when the flag
is set, by making it imply hasNonArgNonControlEffects.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* b3/B3CheckSpecial.cpp:
(JSC::B3::Air::numB3Args):
(JSC::B3::CheckSpecial::Key::Key):
(JSC::B3::CheckSpecial::Key::dump):
(JSC::B3::CheckSpecial::CheckSpecial):
(JSC::B3::CheckSpecial::hiddenBranch):
(JSC::B3::CheckSpecial::forEachArg):
(JSC::B3::CheckSpecial::generate):
(JSC::B3::CheckSpecial::dumpImpl):
(JSC::B3::CheckSpecial::deepDumpImpl):
* b3/B3CheckSpecial.h:
(JSC::B3::CheckSpecial::Key::Key):
(JSC::B3::CheckSpecial::Key::operator==):
(JSC::B3::CheckSpecial::Key::kind):
(JSC::B3::CheckSpecial::Key::hash):
(JSC::B3::CheckSpecial::Key::opcode): Deleted.
* b3/B3Kind.cpp:
(JSC::B3::Kind::dump):
* b3/air/AirDumpAsJS.cpp:
(JSC::B3::Air::dumpAsJS):
* b3/air/AirFixObviousSpills.cpp:
* b3/air/AirFixPartialRegisterStalls.cpp:
* b3/air/AirGenerate.cpp:
(JSC::B3::Air::generate):
* b3/air/AirHandleCalleeSaves.cpp:
(JSC::B3::Air::handleCalleeSaves):
* b3/air/AirInst.cpp:
(JSC::B3::Air::Inst::jsHash):
(JSC::B3::Air::Inst::dump):
* b3/air/AirInst.h:
(JSC::B3::Air::Inst::Inst):
(JSC::B3::Air::Inst::kind):
(JSC::B3::Air::Inst::operator bool):
(JSC::B3::Air::Inst::opcode): Deleted.
* b3/air/AirInstInlines.h:
(JSC::B3::Air::Inst::extraClobberedRegs):
(JSC::B3::Air::Inst::extraEarlyClobberedRegs):
(JSC::B3::Air::Inst::forEachDefWithExtraClobberedRegs):
(JSC::B3::Air::Inst::reportUsedRegisters):
(JSC::B3::Air::Inst::shouldTryAliasingDef):
* b3/air/AirIteratedRegisterCoalescing.cpp:
* b3/air/AirKind.cpp: Added.
(JSC::B3::Air::Kind::dump):
* b3/air/AirKind.h: Added.
(JSC::B3::Air::Kind::Kind):
(JSC::B3::Air::Kind::operator==):
(JSC::B3::Air::Kind::operator!=):
(JSC::B3::Air::Kind::hash):
(JSC::B3::Air::Kind::operator bool):
* b3/air/AirLowerAfterRegAlloc.cpp:
(JSC::B3::Air::lowerAfterRegAlloc):
* b3/air/AirLowerEntrySwitch.cpp:
(JSC::B3::Air::lowerEntrySwitch):
* b3/air/AirLowerMacros.cpp:
(JSC::B3::Air::lowerMacros):
* b3/air/AirOptimizeBlockOrder.cpp:
(JSC::B3::Air::optimizeBlockOrder):
* b3/air/AirReportUsedRegisters.cpp:
(JSC::B3::Air::reportUsedRegisters):
* b3/air/AirSimplifyCFG.cpp:
(JSC::B3::Air::simplifyCFG):
* b3/air/AirTmpWidth.cpp:
(JSC::B3::Air::TmpWidth::recompute):
* b3/air/AirUseCounts.h:
(JSC::B3::Air::UseCounts::UseCounts):
* b3/air/AirValidate.cpp:
* b3/air/opcode_generator.rb:
* b3/testb3.cpp:
(JSC::B3::testTernarySubInstructionSelection):
(JSC::B3::testBranchBitAndImmFusion):

Source/WTF:

* wtf/CommaPrinter.h:
(WTF::CommaPrinter::CommaPrinter):
(WTF::CommaPrinter::dump):
(WTF::CommaPrinter::didPrint):

Websites/webkit.org:

* docs/b3/assembly-intermediate-representation.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@206640 268f45cc-cd09-0410-ab3c-d52691b4dbfc

32 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/b3/B3CheckSpecial.cpp
Source/JavaScriptCore/b3/B3CheckSpecial.h
Source/JavaScriptCore/b3/B3Kind.cpp
Source/JavaScriptCore/b3/air/AirDumpAsJS.cpp
Source/JavaScriptCore/b3/air/AirFixObviousSpills.cpp
Source/JavaScriptCore/b3/air/AirFixPartialRegisterStalls.cpp
Source/JavaScriptCore/b3/air/AirGenerate.cpp
Source/JavaScriptCore/b3/air/AirHandleCalleeSaves.cpp
Source/JavaScriptCore/b3/air/AirInst.cpp
Source/JavaScriptCore/b3/air/AirInst.h
Source/JavaScriptCore/b3/air/AirInstInlines.h
Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp
Source/JavaScriptCore/b3/air/AirKind.cpp [new file with mode: 0644]
Source/JavaScriptCore/b3/air/AirKind.h [new file with mode: 0644]
Source/JavaScriptCore/b3/air/AirLowerAfterRegAlloc.cpp
Source/JavaScriptCore/b3/air/AirLowerEntrySwitch.cpp
Source/JavaScriptCore/b3/air/AirLowerMacros.cpp
Source/JavaScriptCore/b3/air/AirOptimizeBlockOrder.cpp
Source/JavaScriptCore/b3/air/AirReportUsedRegisters.cpp
Source/JavaScriptCore/b3/air/AirSimplifyCFG.cpp
Source/JavaScriptCore/b3/air/AirTmpWidth.cpp
Source/JavaScriptCore/b3/air/AirUseCounts.h
Source/JavaScriptCore/b3/air/AirValidate.cpp
Source/JavaScriptCore/b3/air/opcode_generator.rb
Source/JavaScriptCore/b3/testb3.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/CommaPrinter.h
Websites/webkit.org/ChangeLog
Websites/webkit.org/docs/b3/assembly-intermediate-representation.html

index b74dffd..ac473be 100644 (file)
@@ -88,6 +88,7 @@ set(JavaScriptCore_SOURCES
     b3/air/AirInsertionSet.cpp
     b3/air/AirInst.cpp
     b3/air/AirIteratedRegisterCoalescing.cpp
+    b3/air/AirKind.cpp
     b3/air/AirLogRegisterPressure.cpp
     b3/air/AirLowerAfterRegAlloc.cpp
     b3/air/AirLowerEntrySwitch.cpp
index 3255c67..7aee414 100644 (file)
@@ -1,5 +1,96 @@
 2016-09-29  Filip Pizlo  <fpizlo@apple.com>
 
+        Air should have a way of expressing additional instruction flags
+        https://bugs.webkit.org/show_bug.cgi?id=162699
+
+        Reviewed by Mark Lam.
+        
+        This follows a similar change in B3 (r206595) and replaces Air::Opcode with Air::Kind,
+        which holds onto the opcode and some additional flags. Because Air is an orthogonal ISA
+        (the opcode tells you what the operation does but each operand is allowed to also contain
+        effectively instructions for what to do to read or write that operand), the flags are
+        meant to be orthogonal to opcode. This allows us to say things like Add32<Trap>, which
+        makes sense if any of the operands to the Add32 are addresses.
+        
+        To demonstrate the flags facility this partly adds a trap flag to Air. B3 doesn't use it
+        yet, but I made sure that Air respects it. Basically that means blocking DCE when the flag
+        is set, by making it imply hasNonArgNonControlEffects.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * b3/B3CheckSpecial.cpp:
+        (JSC::B3::Air::numB3Args):
+        (JSC::B3::CheckSpecial::Key::Key):
+        (JSC::B3::CheckSpecial::Key::dump):
+        (JSC::B3::CheckSpecial::CheckSpecial):
+        (JSC::B3::CheckSpecial::hiddenBranch):
+        (JSC::B3::CheckSpecial::forEachArg):
+        (JSC::B3::CheckSpecial::generate):
+        (JSC::B3::CheckSpecial::dumpImpl):
+        (JSC::B3::CheckSpecial::deepDumpImpl):
+        * b3/B3CheckSpecial.h:
+        (JSC::B3::CheckSpecial::Key::Key):
+        (JSC::B3::CheckSpecial::Key::operator==):
+        (JSC::B3::CheckSpecial::Key::kind):
+        (JSC::B3::CheckSpecial::Key::hash):
+        (JSC::B3::CheckSpecial::Key::opcode): Deleted.
+        * b3/B3Kind.cpp:
+        (JSC::B3::Kind::dump):
+        * b3/air/AirDumpAsJS.cpp:
+        (JSC::B3::Air::dumpAsJS):
+        * b3/air/AirFixObviousSpills.cpp:
+        * b3/air/AirFixPartialRegisterStalls.cpp:
+        * b3/air/AirGenerate.cpp:
+        (JSC::B3::Air::generate):
+        * b3/air/AirHandleCalleeSaves.cpp:
+        (JSC::B3::Air::handleCalleeSaves):
+        * b3/air/AirInst.cpp:
+        (JSC::B3::Air::Inst::jsHash):
+        (JSC::B3::Air::Inst::dump):
+        * b3/air/AirInst.h:
+        (JSC::B3::Air::Inst::Inst):
+        (JSC::B3::Air::Inst::kind):
+        (JSC::B3::Air::Inst::operator bool):
+        (JSC::B3::Air::Inst::opcode): Deleted.
+        * b3/air/AirInstInlines.h:
+        (JSC::B3::Air::Inst::extraClobberedRegs):
+        (JSC::B3::Air::Inst::extraEarlyClobberedRegs):
+        (JSC::B3::Air::Inst::forEachDefWithExtraClobberedRegs):
+        (JSC::B3::Air::Inst::reportUsedRegisters):
+        (JSC::B3::Air::Inst::shouldTryAliasingDef):
+        * b3/air/AirIteratedRegisterCoalescing.cpp:
+        * b3/air/AirKind.cpp: Added.
+        (JSC::B3::Air::Kind::dump):
+        * b3/air/AirKind.h: Added.
+        (JSC::B3::Air::Kind::Kind):
+        (JSC::B3::Air::Kind::operator==):
+        (JSC::B3::Air::Kind::operator!=):
+        (JSC::B3::Air::Kind::hash):
+        (JSC::B3::Air::Kind::operator bool):
+        * b3/air/AirLowerAfterRegAlloc.cpp:
+        (JSC::B3::Air::lowerAfterRegAlloc):
+        * b3/air/AirLowerEntrySwitch.cpp:
+        (JSC::B3::Air::lowerEntrySwitch):
+        * b3/air/AirLowerMacros.cpp:
+        (JSC::B3::Air::lowerMacros):
+        * b3/air/AirOptimizeBlockOrder.cpp:
+        (JSC::B3::Air::optimizeBlockOrder):
+        * b3/air/AirReportUsedRegisters.cpp:
+        (JSC::B3::Air::reportUsedRegisters):
+        * b3/air/AirSimplifyCFG.cpp:
+        (JSC::B3::Air::simplifyCFG):
+        * b3/air/AirTmpWidth.cpp:
+        (JSC::B3::Air::TmpWidth::recompute):
+        * b3/air/AirUseCounts.h:
+        (JSC::B3::Air::UseCounts::UseCounts):
+        * b3/air/AirValidate.cpp:
+        * b3/air/opcode_generator.rb:
+        * b3/testb3.cpp:
+        (JSC::B3::testTernarySubInstructionSelection):
+        (JSC::B3::testBranchBitAndImmFusion):
+
+2016-09-29  Filip Pizlo  <fpizlo@apple.com>
+
         REGRESSION(r206555): It made Dromaeo/jslib-style-jquery.html crash
         https://bugs.webkit.org/show_bug.cgi?id=162721
 
index 3986a20..c951ffc 100644 (file)
                0FDDBFB61666EEDA00C55FEF /* DFGVariableAccessDataDump.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDDBFB31666EED500C55FEF /* DFGVariableAccessDataDump.h */; };
                0FDF67D21D9C6D27001B9825 /* B3Kind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDF67D11D9C6086001B9825 /* B3Kind.h */; };
                0FDF67D31D9C6D2A001B9825 /* B3Kind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDF67D01D9C6086001B9825 /* B3Kind.cpp */; };
+               0FDF67D61D9DC440001B9825 /* AirKind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDF67D41D9DC43E001B9825 /* AirKind.cpp */; };
+               0FDF67D71D9DC442001B9825 /* AirKind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDF67D51D9DC43E001B9825 /* AirKind.h */; };
                0FDF70851D3F2C2200927449 /* AirLowerEntrySwitch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDF70831D3F2C1F00927449 /* AirLowerEntrySwitch.cpp */; };
                0FDF70861D3F2C2500927449 /* AirLowerEntrySwitch.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDF70841D3F2C1F00927449 /* AirLowerEntrySwitch.h */; };
                0FE050141AA9091100D33B33 /* ArgumentsMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE0500C1AA9091100D33B33 /* ArgumentsMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FDDBFB31666EED500C55FEF /* DFGVariableAccessDataDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGVariableAccessDataDump.h; path = dfg/DFGVariableAccessDataDump.h; sourceTree = "<group>"; };
                0FDF67D01D9C6086001B9825 /* B3Kind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3Kind.cpp; path = b3/B3Kind.cpp; sourceTree = "<group>"; };
                0FDF67D11D9C6086001B9825 /* B3Kind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3Kind.h; path = b3/B3Kind.h; sourceTree = "<group>"; };
+               0FDF67D41D9DC43E001B9825 /* AirKind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirKind.cpp; path = b3/air/AirKind.cpp; sourceTree = "<group>"; };
+               0FDF67D51D9DC43E001B9825 /* AirKind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirKind.h; path = b3/air/AirKind.h; sourceTree = "<group>"; };
                0FDF70831D3F2C1F00927449 /* AirLowerEntrySwitch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirLowerEntrySwitch.cpp; path = b3/air/AirLowerEntrySwitch.cpp; sourceTree = "<group>"; };
                0FDF70841D3F2C1F00927449 /* AirLowerEntrySwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirLowerEntrySwitch.h; path = b3/air/AirLowerEntrySwitch.h; sourceTree = "<group>"; };
                0FE0500C1AA9091100D33B33 /* ArgumentsMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArgumentsMode.h; sourceTree = "<group>"; };
                                0FEC855C1BDACDC70080FF74 /* AirInstInlines.h */,
                                26718BA21BE99F780052017B /* AirIteratedRegisterCoalescing.cpp */,
                                26718BA31BE99F780052017B /* AirIteratedRegisterCoalescing.h */,
+                               0FDF67D41D9DC43E001B9825 /* AirKind.cpp */,
+                               0FDF67D51D9DC43E001B9825 /* AirKind.h */,
                                2684D4371C00161C0081D663 /* AirLiveness.h */,
                                0FE34C171C4B39AE0003A512 /* AirLogRegisterPressure.cpp */,
                                0FE34C181C4B39AE0003A512 /* AirLogRegisterPressure.h */,
                                A7D89CFE17A0B8CC00773AD8 /* DFGOSRAvailabilityAnalysisPhase.h in Headers */,
                                0FD82E57141DAF1000179C94 /* DFGOSREntry.h in Headers */,
                                0F40E4A71C497F7400A577FA /* AirOpcode.h in Headers */,
+                               0FDF67D71D9DC442001B9825 /* AirKind.h in Headers */,
                                0FD8A32617D51F5700CA2C40 /* DFGOSREntrypointCreationPhase.h in Headers */,
                                0FC0976A1468A6F700CF2442 /* DFGOSRExit.h in Headers */,
                                0F235BEC17178E7300690C7F /* DFGOSRExitBase.h in Headers */,
                                0F4570381BE44C910062A629 /* AirEliminateDeadCode.cpp in Sources */,
                                43AB26C71C1A535C00D82AE6 /* B3MathExtras.cpp in Sources */,
                                0FEC85781BDACDC70080FF74 /* AirGenerate.cpp in Sources */,
+                               0FDF67D61D9DC440001B9825 /* AirKind.cpp in Sources */,
                                0FEC85931BDB1E100080FF74 /* AirGenerated.cpp in Sources */,
                                53486BBB1C18E84500F6F3AF /* JSTypedArray.cpp in Sources */,
                                0FEC857B1BDACDC70080FF74 /* AirHandleCalleeSaves.cpp in Sources */,
index c643959..8661bbd 100644 (file)
@@ -40,9 +40,9 @@ using namespace Air;
 
 namespace {
 
-unsigned numB3Args(B3::Opcode opcode)
+unsigned numB3Args(B3::Kind kind)
 {
-    switch (opcode) {
+    switch (kind.opcode()) {
     case CheckAdd:
     case CheckSub:
     case CheckMul:
@@ -57,7 +57,7 @@ unsigned numB3Args(B3::Opcode opcode)
 
 unsigned numB3Args(Value* value)
 {
-    return numB3Args(value->opcode());
+    return numB3Args(value->kind());
 }
 
 unsigned numB3Args(Inst& inst)
@@ -69,26 +69,26 @@ unsigned numB3Args(Inst& inst)
 
 CheckSpecial::Key::Key(const Inst& inst)
 {
-    m_opcode = inst.opcode;
+    m_kind = inst.kind;
     m_numArgs = inst.args.size();
     m_stackmapRole = SameAsRep;
 }
 
 void CheckSpecial::Key::dump(PrintStream& out) const
 {
-    out.print(m_opcode, "(", m_numArgs, ",", m_stackmapRole, ")");
+    out.print(m_kind, "(", m_numArgs, ",", m_stackmapRole, ")");
 }
 
-CheckSpecial::CheckSpecial(Air::Opcode opcode, unsigned numArgs, RoleMode stackmapRole)
-    : m_checkOpcode(opcode)
+CheckSpecial::CheckSpecial(Air::Kind kind, unsigned numArgs, RoleMode stackmapRole)
+    : m_checkKind(kind)
     , m_stackmapRole(stackmapRole)
     , m_numCheckArgs(numArgs)
 {
-    ASSERT(isDefinitelyTerminal(opcode));
+    ASSERT(isDefinitelyTerminal(kind.opcode));
 }
 
 CheckSpecial::CheckSpecial(const CheckSpecial::Key& key)
-    : CheckSpecial(key.opcode(), key.numArgs(), key.stackmapRole())
+    : CheckSpecial(key.kind(), key.numArgs(), key.stackmapRole())
 {
 }
 
@@ -98,7 +98,7 @@ CheckSpecial::~CheckSpecial()
 
 Inst CheckSpecial::hiddenBranch(const Inst& inst) const
 {
-    Inst hiddenBranch(m_checkOpcode, inst.origin);
+    Inst hiddenBranch(m_checkKind, inst.origin);
     hiddenBranch.args.reserveInitialCapacity(m_numCheckArgs);
     for (unsigned i = 0; i < m_numCheckArgs; ++i)
         hiddenBranch.args.append(inst.args[i + 1]);
@@ -116,7 +116,7 @@ void CheckSpecial::forEachArg(Inst& inst, const ScopedLambda<Inst::EachArgCallba
         });
 
     Optional<unsigned> firstRecoverableIndex;
-    if (m_checkOpcode == BranchAdd32 || m_checkOpcode == BranchAdd64)
+    if (m_checkKind.opcode == BranchAdd32 || m_checkKind.opcode == BranchAdd64)
         firstRecoverableIndex = 1;
     forEachArgImpl(numB3Args(inst), m_numCheckArgs + 1, inst, m_stackmapRole, firstRecoverableIndex, callback);
 }
@@ -164,7 +164,7 @@ CCallHelpers::Jump CheckSpecial::generate(Inst& inst, CCallHelpers& jit, Generat
                 fail.link(&jit);
 
                 // If necessary, undo the operation.
-                switch (m_checkOpcode) {
+                switch (m_checkKind.opcode) {
                 case BranchAdd32:
                     if ((m_numCheckArgs == 4 && args[1] == args[2] && args[2] == args[3])
                         || (m_numCheckArgs == 3 && args[1] == args[2])) {
@@ -235,12 +235,12 @@ CCallHelpers::Jump CheckSpecial::generate(Inst& inst, CCallHelpers& jit, Generat
 
 void CheckSpecial::dumpImpl(PrintStream& out) const
 {
-    out.print(m_checkOpcode, "(", m_numCheckArgs, ",", m_stackmapRole, ")");
+    out.print(m_checkKind, "(", m_numCheckArgs, ",", m_stackmapRole, ")");
 }
 
 void CheckSpecial::deepDumpImpl(PrintStream& out) const
 {
-    out.print("B3::CheckValue lowered to ", m_checkOpcode, " with ", m_numCheckArgs, " args.");
+    out.print("B3::CheckValue lowered to ", m_checkKind, " with ", m_numCheckArgs, " args.");
 }
 
 } } // namespace JSC::B3
index c7ecf77..aec1897 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,7 +28,7 @@
 #if ENABLE(B3_JIT)
 
 #include "AirArg.h"
-#include "AirOpcode.h"
+#include "AirKind.h"
 #include "B3StackmapSpecial.h"
 #include <wtf/HashMap.h>
 
@@ -55,14 +55,13 @@ public:
     class Key {
     public:
         Key()
-            : m_opcode(Air::Nop)
-            , m_stackmapRole(SameAsRep)
+            : m_stackmapRole(SameAsRep)
             , m_numArgs(0)
         {
         }
         
-        Key(Air::Opcode opcode, unsigned numArgs, RoleMode stackmapRole = SameAsRep)
-            : m_opcode(opcode)
+        Key(Air::Kind kind, unsigned numArgs, RoleMode stackmapRole = SameAsRep)
+            : m_kind(kind)
             , m_stackmapRole(stackmapRole)
             , m_numArgs(numArgs)
         {
@@ -72,7 +71,7 @@ public:
 
         bool operator==(const Key& other) const
         {
-            return m_opcode == other.m_opcode
+            return m_kind == other.m_kind
                 && m_numArgs == other.m_numArgs
                 && m_stackmapRole == other.m_stackmapRole;
         }
@@ -84,15 +83,14 @@ public:
 
         explicit operator bool() const { return *this != Key(); }
 
-        Air::Opcode opcode() const { return m_opcode; }
+        Air::Kind kind() const { return m_kind; }
         unsigned numArgs() const { return m_numArgs; }
         RoleMode stackmapRole() const { return m_stackmapRole; }
 
         void dump(PrintStream& out) const;
 
         Key(WTF::HashTableDeletedValueType)
-            : m_opcode(Air::Nop)
-            , m_stackmapRole(SameAsRep)
+            : m_stackmapRole(SameAsRep)
             , m_numArgs(1)
         {
         }
@@ -105,16 +103,16 @@ public:
         unsigned hash() const
         {
             // Seriously, we don't need to be smart here. It just doesn't matter.
-            return m_opcode + m_numArgs + m_stackmapRole;
+            return m_kind.hash() + m_numArgs + m_stackmapRole;
         }
         
     private:
-        Air::Opcode m_opcode;
+        Air::Kind m_kind;
         RoleMode m_stackmapRole;
         unsigned m_numArgs;
     };
     
-    CheckSpecial(Air::Opcode, unsigned numArgs, RoleMode stackmapRole = SameAsRep);
+    CheckSpecial(Air::Kind, unsigned numArgs, RoleMode stackmapRole = SameAsRep);
     CheckSpecial(const Key&);
     ~CheckSpecial();
 
@@ -136,7 +134,7 @@ protected:
     void deepDumpImpl(PrintStream&) const override;
 
 private:
-    Air::Opcode m_checkOpcode;
+    Air::Kind m_checkKind;
     RoleMode m_stackmapRole;
     unsigned m_numCheckArgs;
 };
index c04a298..401af6a 100644 (file)
@@ -35,14 +35,12 @@ namespace JSC { namespace B3 {
 void Kind::dump(PrintStream& out) const
 {
     out.print(m_opcode);
-    if (!hasExtraBits())
-        return;
     
-    CommaPrinter comma;
-    out.print("<");
+    CommaPrinter comma(", ", "<");
     if (isChill())
         out.print(comma, "Chill");
-    out.print(">");
+    if (comma.didPrint())
+        out.print(">");
 }
 
 } } // namespace JSC::B3
index 029101b..3d8d6fb 100644 (file)
@@ -118,7 +118,9 @@ void dumpAsJS(Code& code, PrintStream& out)
             out.println(varName(block), ".predecessors.push(", varName(predecessor), ");");
         
         for (Inst& inst : *block) {
-            out.println("inst = new Inst(", inst.opcode, ");");
+            // FIXME: This should do something for flags.
+            // https://bugs.webkit.org/show_bug.cgi?id=162751
+            out.println("inst = new Inst(", inst.kind.opcode, ");");
             
             inst.forEachArg(
                 [&] (Arg& arg, Arg::Role, Arg::Type, Arg::Width) {
@@ -197,7 +199,7 @@ void dumpAsJS(Code& code, PrintStream& out)
                     out.println("inst.args.push(arg);");
                 });
             
-            if (inst.opcode == Patch) {
+            if (inst.kind.opcode == Patch) {
                 if (inst.hasNonArgEffects())
                     out.println("inst.patchHasNonArgEffects = true;");
                 
@@ -221,7 +223,7 @@ void dumpAsJS(Code& code, PrintStream& out)
                     });
             }
             
-            if (inst.opcode == CCall || inst.opcode == ColdCCall) {
+            if (inst.kind.opcode == CCall || inst.kind.opcode == ColdCCall) {
                 out.println("inst.cCallType = ", inst.origin->type());
                 out.println("inst.cCallArgTypes = [];");
                 for (unsigned i = 1; i < inst.origin->numChildren(); ++i)
index babd173..8bcb538 100644 (file)
@@ -121,7 +121,7 @@ private:
                 m_state.clobber(arg);
             });
 
-        switch (inst.opcode) {
+        switch (inst.kind.opcode) {
         case Move:
             if (inst.args[0].isSomeImm()) {
                 if (inst.args[1].isReg())
index 5399799..b3d5d0b 100644 (file)
@@ -45,7 +45,7 @@ namespace {
 
 bool hasPartialXmmRegUpdate(const Inst& inst)
 {
-    switch (inst.opcode) {
+    switch (inst.kind.opcode) {
     case ConvertDoubleToFloat:
     case ConvertFloatToDouble:
     case ConvertInt32ToDouble:
@@ -68,7 +68,7 @@ bool hasPartialXmmRegUpdate(const Inst& inst)
 bool isDependencyBreaking(const Inst& inst)
 {
     // "xorps reg, reg" is used by the frontend to remove the dependency on its argument.
-    return inst.opcode == MoveZeroToDouble;
+    return inst.kind.opcode == MoveZeroToDouble;
 }
 
 // FIXME: find a good distance per architecture experimentally.
index 0a18ada..82c5681 100644 (file)
@@ -227,13 +227,13 @@ void generate(Code& code, CCallHelpers& jit)
 
         context.indexInBlock = block->size() - 1;
         
-        if (block->last().opcode == Jump
+        if (block->last().kind.opcode == Jump
             && block->successorBlock(0) == code.findNextBlock(block))
             continue;
 
         addItem(block->last());
 
-        if (isReturn(block->last().opcode)) {
+        if (isReturn(block->last().kind.opcode)) {
             // We currently don't represent the full prologue/epilogue in Air, so we need to
             // have this override.
             if (code.frameSize()) {
index 90b2267..11c7aa4 100644 (file)
@@ -48,7 +48,7 @@ void handleCalleeSaves(Code& code)
                     usedCalleeSaves.set(tmp.reg());
                 });
 
-            if (inst.opcode == Patch)
+            if (inst.kind.opcode == Patch)
                 usedCalleeSaves.merge(inst.extraClobberedRegs());
         }
     }
index e83c3a4..defb344 100644 (file)
@@ -47,7 +47,9 @@ bool Inst::hasArgEffects()
 
 unsigned Inst::jsHash() const
 {
-    unsigned result = static_cast<unsigned>(opcode);
+    // FIXME: This should do something for flags.
+    // https://bugs.webkit.org/show_bug.cgi?id=162751
+    unsigned result = static_cast<unsigned>(kind.opcode);
     
     for (const Arg& arg : args)
         result += arg.jsHash();
@@ -57,7 +59,7 @@ unsigned Inst::jsHash() const
 
 void Inst::dump(PrintStream& out) const
 {
-    out.print(opcode, " ", listDump(args));
+    out.print(kind, " ", listDump(args));
     if (origin) {
         if (args.size())
             out.print(", ");
index 34468f0..b1dc9cb 100644 (file)
@@ -28,7 +28,7 @@
 #if ENABLE(B3_JIT)
 
 #include "AirArg.h"
-#include "AirOpcode.h"
+#include "AirKind.h"
 #include "CCallHelpers.h"
 
 namespace JSC {
@@ -50,39 +50,38 @@ public:
 
     Inst()
         : origin(nullptr)
-        , opcode(Nop)
     {
     }
     
-    Inst(Opcode opcode, Value* origin)
+    Inst(Kind kind, Value* origin)
         : origin(origin)
-        , opcode(opcode)
+        , kind(kind)
     {
     }
     
     template<typename... Arguments>
-    Inst(Opcode opcode, Value* origin, Arg arg, Arguments... arguments)
+    Inst(Kind kind, Value* origin, Arg arg, Arguments... arguments)
         : args{ arg, arguments... }
         , origin(origin)
-        , opcode(opcode)
+        , kind(kind)
     {
     }
 
-    Inst(Opcode opcode, Value* origin, const ArgList& arguments)
+    Inst(Kind kind, Value* origin, const ArgList& arguments)
         : args(arguments)
         , origin(origin)
-        , opcode(opcode)
+        , kind(kind)
     {
     }
 
-    Inst(Opcode opcode, Value* origin, ArgList&& arguments)
+    Inst(Kind kind, Value* origin, ArgList&& arguments)
         : args(WTFMove(arguments))
         , origin(origin)
-        , opcode(opcode)
+        , kind(kind)
     {
     }
 
-    explicit operator bool() const { return origin || opcode != Nop || args.size(); }
+    explicit operator bool() const { return origin || kind || args.size(); }
 
     void append() { }
     
@@ -200,7 +199,7 @@ public:
 
     ArgList args;
     Value* origin; // The B3::Value that this originated from.
-    Opcode opcode;
+    Kind kind;
 };
 
 } } } // namespace JSC::B3::Air
index c76d6da..9249492 100644 (file)
@@ -46,13 +46,13 @@ void Inst::forEach(const Functor& functor)
 
 inline const RegisterSet& Inst::extraClobberedRegs()
 {
-    ASSERT(opcode == Patch);
+    ASSERT(kind.opcode == Patch);
     return args[0].special()->extraClobberedRegs(*this);
 }
 
 inline const RegisterSet& Inst::extraEarlyClobberedRegs()
 {
-    ASSERT(opcode == Patch);
+    ASSERT(kind.opcode == Patch);
     return args[0].special()->extraEarlyClobberedRegs(*this);
 }
 
@@ -89,12 +89,12 @@ inline void Inst::forEachDefWithExtraClobberedRegs(
         functor(Thing(reg), regDefRole, type, Arg::conservativeWidth(type));
     };
 
-    if (prevInst && prevInst->opcode == Patch) {
+    if (prevInst && prevInst->kind.opcode == Patch) {
         regDefRole = Arg::Def;
         prevInst->extraClobberedRegs().forEach(reportReg);
     }
 
-    if (nextInst && nextInst->opcode == Patch) {
+    if (nextInst && nextInst->kind.opcode == Patch) {
         regDefRole = Arg::EarlyDef;
         nextInst->extraEarlyClobberedRegs().forEach(reportReg);
     }
@@ -102,7 +102,7 @@ inline void Inst::forEachDefWithExtraClobberedRegs(
 
 inline void Inst::reportUsedRegisters(const RegisterSet& usedRegisters)
 {
-    ASSERT(opcode == Patch);
+    ASSERT(kind.opcode == Patch);
     args[0].special()->reportUsedRegisters(*this, usedRegisters);
 }
 
@@ -116,7 +116,7 @@ inline Optional<unsigned> Inst::shouldTryAliasingDef()
     if (!isX86())
         return Nullopt;
 
-    switch (opcode) {
+    switch (kind.opcode) {
     case Add32:
     case Add64:
     case And32:
index 6f790d1..2989d52 100644 (file)
@@ -1040,7 +1040,7 @@ private:
     {
         switch (type) {
         case Arg::GP:
-            switch (inst.opcode) {
+            switch (inst.kind.opcode) {
             case Move:
             case Move32:
                 break;
@@ -1049,7 +1049,7 @@ private:
             }
             break;
         case Arg::FP:
-            switch (inst.opcode) {
+            switch (inst.kind.opcode) {
             case MoveFloat:
             case MoveDouble:
                 break;
@@ -1074,7 +1074,7 @@ private:
         // Note that the input property requires an analysis over ZDef's, so it's only valid so long
         // as the input gets a register. We don't know if the input gets a register, but we do know
         // that if it doesn't get a register then we will still emit this Move32.
-        if (inst.opcode == Move32) {
+        if (inst.kind.opcode == Move32) {
             if (!tmpWidth)
                 return false;
 
@@ -1382,11 +1382,11 @@ private:
                 // equivalent if the destination's high bits are not observable or if the source's high
                 // bits are all zero. Note that we don't have the opposite optimization for other
                 // architectures, which may prefer Move over Move32, because Move is canonical already.
-                if (type == Arg::GP && inst.opcode == Move
+                if (type == Arg::GP && inst.kind.opcode == Move
                     && inst.args[0].isTmp() && inst.args[1].isTmp()) {
                     if (m_tmpWidth.useWidth(inst.args[1].tmp()) <= Arg::Width32
                         || m_tmpWidth.defWidth(inst.args[0].tmp()) <= Arg::Width32)
-                        inst.opcode = Move32;
+                        inst.kind.opcode = Move32;
                 }
 
                 inst.forEachTmpFast([&] (Tmp& tmp) {
@@ -1453,7 +1453,7 @@ private:
                 // Move is the canonical way to move data between GPRs.
                 bool canUseMove32IfDidSpill = false;
                 bool didSpill = false;
-                if (type == Arg::GP && inst.opcode == Move) {
+                if (type == Arg::GP && inst.kind.opcode == Move) {
                     if ((inst.args[0].isTmp() && m_tmpWidth.width(inst.args[0].tmp()) <= Arg::Width32)
                         || (inst.args[1].isTmp() && m_tmpWidth.width(inst.args[1].tmp()) <= Arg::Width32))
                         canUseMove32IfDidSpill = true;
@@ -1488,7 +1488,7 @@ private:
                         Arg::Width spillWidth = m_tmpWidth.requiredWidth(arg.tmp());
                         if (Arg::isAnyDef(role) && width < spillWidth)
                             return;
-                        ASSERT(inst.opcode == Move || !(Arg::isAnyUse(role) && width > spillWidth));
+                        ASSERT(inst.kind.opcode == Move || !(Arg::isAnyUse(role) && width > spillWidth));
                         
                         if (spillWidth != Arg::Width32)
                             canUseMove32IfDidSpill = false;
@@ -1500,7 +1500,7 @@ private:
                     });
 
                 if (didSpill && canUseMove32IfDidSpill)
-                    inst.opcode = Move32;
+                    inst.kind.opcode = Move32;
 
                 // For every other case, add Load/Store as needed.
                 inst.forEachTmp([&] (Tmp& tmp, Arg::Role role, Arg::Type argType, Arg::Width) {
diff --git a/Source/JavaScriptCore/b3/air/AirKind.cpp b/Source/JavaScriptCore/b3/air/AirKind.cpp
new file mode 100644 (file)
index 0000000..9fe2525
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "AirKind.h"
+
+#if ENABLE(B3_JIT)
+
+#include <wtf/CommaPrinter.h>
+
+namespace JSC { namespace B3 { namespace Air {
+
+void Kind::dump(PrintStream& out) const
+{
+    out.print(opcode);
+    
+    CommaPrinter comma(", ", "<");
+    if (traps)
+        out.print(comma, "Traps");
+    if (comma.didPrint())
+        out.print(">");
+}
+
+} } } // namespace JSC::B3::Air
+
+#endif // ENABLE(B3_JIT)
+
diff --git a/Source/JavaScriptCore/b3/air/AirKind.h b/Source/JavaScriptCore/b3/air/AirKind.h
new file mode 100644 (file)
index 0000000..e723d46
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef AirKind_h
+#define AirKind_h
+
+#if ENABLE(B3_JIT)
+
+#include "AirOpcode.h"
+#include <wtf/PrintStream.h>
+
+namespace JSC { namespace B3 { namespace Air {
+
+// Air opcodes are always carried around with some flags. These flags are understood as having no
+// meaning if they are set for an opcode to which they do not apply. This makes sense, since Air
+// is a complex instruction set and most of these flags can apply to basically any opcode. In
+// fact, it's recommended to only represent something as a flag if you believe that it is largely
+// opcode-agnostic.
+
+struct Kind {
+    Kind(Opcode opcode)
+        : opcode(opcode)
+        , traps(false)
+    {
+    }
+    
+    Kind()
+        : Kind(Nop)
+    {
+    }
+    
+    bool operator==(const Kind& other) const
+    {
+        return opcode == other.opcode
+            && traps == other.traps;
+    }
+    
+    bool operator!=(const Kind& other) const
+    {
+        return !(*this == other);
+    }
+    
+    unsigned hash() const
+    {
+        return static_cast<unsigned>(opcode) + (static_cast<unsigned>(traps) << 16);
+    }
+    
+    explicit operator bool() const
+    {
+        return *this != Kind();
+    }
+    
+    void dump(PrintStream&) const;
+    
+    Opcode opcode;
+    
+    // This is an opcode-agnostic flag that indicates that we expect that this instruction will
+    // trap. This causes the compiler to assume that this side-exits and therefore has non-control
+    // non-arg effects. This also causes the compiler to tell you about all of these instructions.
+    // Note that this is just one of several ways of supporting trapping in Air, and it's the less
+    // precise variant because it's origin-based. This means that if an instruction was fused out
+    // of B3 values that had different origins, then the origin at which you'll appear to trap
+    // will be somewhat random. The upside of this approach is that it imposes by far the least
+    // overhead on the compiler.
+    // FIXME: Make this completely work.
+    // https://bugs.webkit.org/show_bug.cgi?id=162689
+    bool traps : 1;
+};
+
+} } } // namespace JSC::B3::Air
+
+#endif // ENABLE(B3_JIT)
+
+#endif // AirKind_h
+
index 0d2e40b..a4a19a1 100644 (file)
@@ -68,7 +68,7 @@ void lowerAfterRegAlloc(Code& code)
             
             RegisterSet set;
 
-            bool isRelevant = inst.opcode == Shuffle || inst.opcode == ColdCCall;
+            bool isRelevant = inst.kind.opcode == Shuffle || inst.kind.opcode == ColdCCall;
             
             if (isRelevant) {
                 for (Reg reg : localCalc.live())
@@ -110,7 +110,7 @@ void lowerAfterRegAlloc(Code& code)
         for (unsigned instIndex = 0; instIndex < block->size(); ++instIndex) {
             Inst& inst = block->at(instIndex);
 
-            switch (inst.opcode) {
+            switch (inst.kind.opcode) {
             case Shuffle: {
                 RegisterSet set = usedRegisters.get(&inst);
                 Vector<ShufflePair> pairs;
@@ -141,6 +141,7 @@ void lowerAfterRegAlloc(Code& code)
 
             case ColdCCall: {
                 CCallValue* value = inst.origin->as<CCallValue>();
+                Kind oldKind = inst.kind;
 
                 RegisterSet liveRegs = usedRegisters.get(&inst);
                 RegisterSet regsToSave = liveRegs;
@@ -195,6 +196,8 @@ void lowerAfterRegAlloc(Code& code)
                     instIndex, emitShuffle(pairs, gpScratch, fpScratch, inst.origin));
 
                 inst = buildCCall(code, inst.origin, destinations);
+                if (oldKind.traps)
+                    inst.kind.traps = true;
 
                 // Now we need to emit code to restore registers.
                 pairs.resize(0);
index b43ab71..e14641d 100644 (file)
@@ -43,7 +43,7 @@ void lowerEntrySwitch(Code& code)
     // Figure out the set of blocks that should be duplicated.
     BlockWorklist worklist;
     for (BasicBlock* block : code) {
-        if (block->last().opcode == EntrySwitch)
+        if (block->last().kind.opcode == EntrySwitch)
             worklist.push(block);
     }
     
@@ -61,7 +61,7 @@ void lowerEntrySwitch(Code& code)
     
     Vector<FrequencyClass> entrypointFrequencies(code.proc().numEntrypoints(), FrequencyClass::Rare);
     for (BasicBlock* block : code) {
-        if (block->last().opcode != EntrySwitch)
+        if (block->last().kind.opcode != EntrySwitch)
             continue;
         for (unsigned entrypointIndex = code.proc().numEntrypoints(); entrypointIndex--;) {
             entrypointFrequencies[entrypointIndex] = maxFrequency(
@@ -71,10 +71,10 @@ void lowerEntrySwitch(Code& code)
     }
     
     auto fixEntrySwitch = [&] (BasicBlock* block, unsigned entrypointIndex) {
-        if (block->last().opcode != EntrySwitch)
+        if (block->last().kind.opcode != EntrySwitch)
             return;
         FrequentedBlock target = block->successor(entrypointIndex);
-        block->last().opcode = Jump;
+        block->last().kind.opcode = Jump;
         block->successors().resize(1);
         block->successor(0) = target;
     };
index 12bd89d..b086b7b 100644 (file)
@@ -47,9 +47,10 @@ void lowerMacros(Code& code)
         for (unsigned instIndex = 0; instIndex < block->size(); ++instIndex) {
             Inst& inst = block->at(instIndex);
 
-            switch (inst.opcode) {
+            switch (inst.kind.opcode) {
             case CCall: {
                 CCallValue* value = inst.origin->as<CCallValue>();
+                Kind oldKind = inst.kind;
 
                 Vector<Arg> destinations = computeCCallingConvention(code, value);
 
@@ -70,6 +71,8 @@ void lowerMacros(Code& code)
                 Arg resultDst = value->type() == Void ? Arg() : inst.args[1];
                 
                 inst = buildCCall(code, inst.origin, destinations);
+                if (oldKind.traps)
+                    inst.kind.traps = true;
 
                 Tmp result = cCallResult(value->type());
                 switch (value->type()) {
index d05aadb..11ca3f3 100644 (file)
@@ -160,7 +160,7 @@ void optimizeBlockOrder(Code& code)
         // optimization.  You'll probably realize that as soon as you look at the disassembly, and it
         // certainly won't cause any correctness issues.
         
-        switch (branch.opcode) {
+        switch (branch.kind.opcode) {
         case Branch8:
         case Branch32:
         case Branch64:
index 8ec11fc..bb0aeab 100644 (file)
@@ -73,7 +73,7 @@ void reportUsedRegisters(Code& code)
                     inst = Inst();
             }
             
-            if (inst.opcode == Patch) {
+            if (inst.kind.opcode == Patch) {
                 RegisterSet registerSet;
                 for (Reg reg : localCalc.live())
                     registerSet.set(reg);
index 7566397..c66f63f 100644 (file)
@@ -78,7 +78,7 @@ bool simplifyCFG(Code& code)
             for (BasicBlock*& successor : block->successorBlocks()) {
                 if (successor != block
                     && successor->size() == 1
-                    && successor->last().opcode == Jump) {
+                    && successor->last().kind.opcode == Jump) {
                     BasicBlock* newSuccessor = successor->successorBlock(0);
                     if (newSuccessor != successor) {
                         if (verbose) {
index 7c7227a..f1173c0 100644 (file)
@@ -88,7 +88,7 @@ void TmpWidth::recompute(Code& code)
     Vector<Inst*> moves;
     for (BasicBlock* block : code) {
         for (Inst& inst : *block) {
-            if (inst.opcode == Move && inst.args[1].isTmp()) {
+            if (inst.kind.opcode == Move && inst.args[1].isTmp()) {
                 if (inst.args[0].isTmp()) {
                     // Make sure that both sides of the Move have a width already initialized. The
                     // fixpoint below assumes that it never has to add things to the HashMap.
@@ -135,7 +135,7 @@ void TmpWidth::recompute(Code& code)
     while (changed) {
         changed = false;
         for (Inst* move : moves) {
-            ASSERT(move->opcode == Move);
+            ASSERT(move->kind.opcode == Move);
             ASSERT(move->args[0].isTmp());
             ASSERT(move->args[1].isTmp());
 
index f7e9d3f..98a7493 100644 (file)
@@ -88,7 +88,7 @@ public:
                             counts.numDefs += frequency;
                     });
 
-                if ((inst.opcode == Move || inst.opcode == Move32)
+                if ((inst.kind.opcode == Move || inst.kind.opcode == Move32)
                     && inst.args[0].isSomeImm()
                     && inst.args[1].is<Thing>())
                     m_counts.add(inst.args[1].as<Thing>(), Counts()).iterator->value.numConstDefs++;
index 5e3419b..d90de62 100644 (file)
@@ -95,10 +95,15 @@ public:
                         VALIDATE(&arg <= &inst.args.last(), ("At ", arg, " in ", inst, " in ", *block));
                     });
                 
-                switch (inst.opcode) {
+                switch (inst.kind.opcode) {
                 case EntrySwitch:
                     VALIDATE(block->numSuccessors() == m_code.proc().numEntrypoints(), ("At ", inst, " in ", *block));
                     break;
+                case Shuffle:
+                    // We can't handle trapping shuffles because of how we lower them. That could
+                    // be fixed though.
+                    VALIDATE(!inst.kind.traps, ("At ", inst, " in ", *block));
+                    break;
                 default:
                     break;
                 }
index d0900a3..d142405 100644 (file)
@@ -545,7 +545,7 @@ def matchForms(outp, speed, forms, columnIndex, columnGetter, filter, callback)
 end
 
 def matchInstOverload(outp, speed, inst)
-    outp.puts "switch (#{inst}->opcode) {"
+    outp.puts "switch (#{inst}->kind.opcode) {"
     $opcodes.values.each {
         | opcode |
         outp.puts "case #{opcode.name}:"
@@ -836,7 +836,7 @@ writeH("OpcodeGenerated") {
 
     outp.puts "bool Inst::admitsStack(unsigned argIndex)"
     outp.puts "{"
-    outp.puts "switch (opcode) {"
+    outp.puts "switch (kind.opcode) {"
     $opcodes.values.each {
         | opcode |
         outp.puts "case #{opcode.name}:"
@@ -978,7 +978,7 @@ writeH("OpcodeGenerated") {
 
     outp.puts "bool Inst::isTerminal()"
     outp.puts "{"
-    outp.puts "switch (opcode) {"
+    outp.puts "switch (kind.opcode) {"
     foundTrue = false
     $opcodes.values.each {
         | opcode |
@@ -1004,7 +1004,9 @@ writeH("OpcodeGenerated") {
     
     outp.puts "bool Inst::hasNonArgNonControlEffects()"
     outp.puts "{"
-    outp.puts "switch (opcode) {"
+    outp.puts "if (kind.traps)"
+    outp.puts "return true;"
+    outp.puts "switch (kind.opcode) {"
     foundTrue = false
     $opcodes.values.each {
         | opcode |
@@ -1030,7 +1032,9 @@ writeH("OpcodeGenerated") {
     
     outp.puts "bool Inst::hasNonArgEffects()"
     outp.puts "{"
-    outp.puts "switch (opcode) {"
+    outp.puts "if (kind.traps)"
+    outp.puts "return true;"
+    outp.puts "switch (kind.opcode) {"
     foundTrue = false
     $opcodes.values.each {
         | opcode |
index 65d6f8c..9853399 100644 (file)
@@ -2217,7 +2217,7 @@ void testTernarySubInstructionSelection(B3::Opcode valueModifier, Type valueType
     auto block = proc.code()[0];
     unsigned numberOfSubInstructions = 0;
     for (auto instruction : *block) {
-        if (instruction.opcode == expectedOpcode) {
+        if (instruction.kind.opcode == expectedOpcode) {
             CHECK_EQ(instruction.args.size(), 3ul);
             CHECK_EQ(instruction.args[0].kind(), Air::Arg::Tmp);
             CHECK_EQ(instruction.args[1].kind(), Air::Arg::Tmp);
@@ -12972,7 +12972,7 @@ void testBranchBitAndImmFusion(
 
     // The first basic block must end in a BranchTest64(resCond, tmp, bitImm).
     Air::Inst terminal = proc.code()[0]->last();
-    CHECK_EQ(terminal.opcode, expectedOpcode);
+    CHECK_EQ(terminal.kind.opcode, expectedOpcode);
     CHECK_EQ(terminal.args[0].kind(), Air::Arg::ResCond);
     CHECK_EQ(terminal.args[1].kind(), firstKind);
     CHECK(terminal.args[2].kind() == Air::Arg::BitImm || terminal.args[2].kind() == Air::Arg::BitImm64);
index 175de13..12eca66 100644 (file)
@@ -1,3 +1,15 @@
+2016-09-29  Filip Pizlo  <fpizlo@apple.com>
+
+        Air should have a way of expressing additional instruction flags
+        https://bugs.webkit.org/show_bug.cgi?id=162699
+
+        Reviewed by Mark Lam.
+
+        * wtf/CommaPrinter.h:
+        (WTF::CommaPrinter::CommaPrinter):
+        (WTF::CommaPrinter::dump):
+        (WTF::CommaPrinter::didPrint):
+
 2016-09-30  Youenn Fablet  <youenn@apple.com>
 
         Add a way to go from a RefPtr<T> to Ref<const T>
index a8f3d39..e6ab9e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -32,25 +32,30 @@ namespace WTF {
 
 class CommaPrinter {
 public:
-    CommaPrinter(const char* comma = ", ")
+    CommaPrinter(const char* comma = ", ", const char* start = "")
         : m_comma(comma)
-        , m_isFirst(true)
+        , m_start(start)
+        , m_didPrint(false)
     {
     }
     
     void dump(PrintStream& out) const
     {
-        if (m_isFirst) {
-            m_isFirst = false;
+        if (!m_didPrint) {
+            out.print(m_start);
+            m_didPrint = true;
             return;
         }
         
         out.print(m_comma);
     }
     
+    bool didPrint() const { return m_didPrint; }
+    
 private:
     const char* m_comma;
-    mutable bool m_isFirst;
+    const char* m_start;
+    mutable bool m_didPrint;
 };
 
 } // namespace WTF
index befb9ef..17b55a4 100644 (file)
@@ -1,3 +1,12 @@
+2016-09-30  Filip Pizlo  <fpizlo@apple.com>
+
+        Air should have a way of expressing additional instruction flags
+        https://bugs.webkit.org/show_bug.cgi?id=162699
+
+        Reviewed by Mark Lam.
+
+        * docs/b3/assembly-intermediate-representation.html:
+
 2016-09-28  Filip Pizlo  <fpizlo@apple.com>
 
         B3 opcodes should leave room for flags
index 032d520..4f2e880 100644 (file)
       <a href="http://trac.webkit.org/browser/trunk/Source/JavaScriptCore/b3/air/AirInst.h"><code>Inst</code></a>s.
       Air has an explicit control flow graph: each basic block has predecessor and successor blocks.
       Execution always begins at the first basic block (<code>code[0]</code>). The <code>Inst</code>s
-      in each block are executed in order. Each <code>Inst</code> has an opcode, an array of
-      arguments
+      in each block are executed in order. Each <code>Inst</code> has an opcode, some flags, an
+      array of arguments
       (<a href="http://trac.webkit.org/browser/trunk/Source/JavaScriptCore/b3/air/AirArg.h"><code>Arg</code></a>s),
-      and an origin. The origin is simply a B3 IR
+      and an origin. The opcode and flags are wrapped in a <code>Kind</code>, to make it
+      convenient to carry them around together. The origin is simply a B3 IR
       <a href="http://trac.webkit.org/browser/trunk/Source/JavaScriptCore/b3/B3Value.h"><code>Value</code></a>.
       Some opcodes use the origin for additional meta-data. This works because Air code always
       coexists with the B3 procedure from which it was generated.</p>
       <li><code>func(%rcx, UseZDef, GP, Width32)</code></li>
     </ol>
     
+    <p>It's important to remember that Air's summaries of what instructions do to arguments are
+      not meant to be exhaustive. For example, if an instruction claims to use an address, this
+      tells you that the instruction will perform a load but it tells you nothing about how that
+      load will be performed. This means that unless you know exactly what it means to use/def an
+      argument, you cannot perform this transformation:</p>
+    
+    <ul>
+      <li>Before:
+        <pre><code>Foo32 (%rax)</code></pre></li>
+      <li>After:
+        <pre><code>Move32 (%rax), %tmp
+Foo32 %tmp</code></pre></li>
+    </ul>
+    
+    <p>Even if you know that Foo32 only uses its argument, you cannot do this because Move32 may
+      not load from the address using exactly the same kind of load that Foo32 would have done.
+      Memory accesses have many dimensions of options: alignment semantics (if you're lucky then
+      misaligned accesses run fast but sometimes they ignore low bits, trap if unaligned, or run
+      super slow when unaligned, and this behavior may depend on the opcode), temporality and
+      memory ordering, determinism of trapping, etc. Just seeing that an instruction uses an
+      address does not tell you what kind of load will happen, and currently Air does not have the
+      ability to answer such questions. Fortunately, Air does not have much need to move memory
+      accesses out of instructions. Uses and defs of temporaries, registers, immediates, and spill
+      slots don't have these caveats and so those arguments can be moved from one instruction to
+      another without worries.</p>
+    
     <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