Title: [216306] trunk/Source/_javascript_Core
Revision
216306
Author
jfbast...@apple.com
Date
2017-05-05 20:57:42 -0700 (Fri, 05 May 2017)

Log Message

WebAssembly: Air::Inst::generate crashes on large binary on A64
https://bugs.webkit.org/show_bug.cgi?id=170215

Reviewed by Filip Pizlo.

ARM can't encode all offsets in a single instruction. We usualy
handle this type of detail early, or the macro assembler uses a
scratch register to take care of the large immediate. After
register allocation we assumed that we would never get large
offsets, and asserted this was the case. That was a fine
assumption with _javascript_, but WebAssembly ends up generating
stack frames which are too big to encode.

There are two places that needed to be fixed:
    1. AirGenerate
    2. AirLowerStackArgs

We now unconditionally pin the dataTempRegister on ARM64, and use
it when immediates don't fit.

Number 1. is easy: we're just incrementing SP, make sure we can
use a scratch register when that happens.

Number 2. is more complex: not all Inst can receive a stack
argument whose base register isn't SP or FP. Specifically,
Patchpoints and Stackmaps get very sad because they just want to
know the offset value, but when we materialize the offset as
follows:

    Move (spill337), (spill201), %r0, @8735

Becomes (where %r16 is dataTempRegister):
    Move $1404, %r16, @8736
    Add64 %sp, %r16, @8736
    Move (%r16), 2032(%sp), %r0, @8736

The code currently doesn't see through our little dance. To work
around this issue we introduce a new Air Arg kind:
ExtendedOffsetAddr. This is the same as a regular Addr, but with
an offset which may be too big to encode. Opcodes then declare
whether their arguments can handle such inputs, and if so we
generate them, otherwise we generate Addr as shown above.

None of this affects x86 because it can always encode large
immediates.

This patch also drive-by converts some uses of `override` to
`final`. It makes the code easier to grok, and maybe helps the
optimizer sometimes but really that doens't matter.

* assembler/MacroAssembler.h:
* assembler/MacroAssemblerARM64.h:
* b3/B3CheckSpecial.cpp:
(JSC::B3::CheckSpecial::admitsExtendedOffsetAddr):
* b3/B3CheckSpecial.h:
* b3/B3Common.cpp:
(JSC::B3::pinnedExtendedOffsetAddrRegister): keep the CPU-specific
pinning information in a cpp file
* b3/B3Common.h:
* b3/B3PatchpointSpecial.cpp:
(JSC::B3::PatchpointSpecial::admitsExtendedOffsetAddr):
* b3/B3PatchpointSpecial.h:
* b3/B3StackmapSpecial.cpp:
(JSC::B3::StackmapSpecial::isArgValidForRep):
(JSC::B3::StackmapSpecial::repForArg):
* b3/B3StackmapSpecial.h:
* b3/air/AirArg.cpp:
(JSC::B3::Air::Arg::isStackMemory):
(JSC::B3::Air::Arg::jsHash):
(JSC::B3::Air::Arg::dump):
(WTF::printInternal):
(JSC::B3::Air::Arg::stackAddrImpl): Deleted. There was only one
use of this (in AirLowerStackArgs) and it was now confusing to
split the logic up between these two. Inline the code that used to
be here into its one usepoint instead.
* b3/air/AirArg.h:
(JSC::B3::Air::Arg::extendedOffsetAddr):
(JSC::B3::Air::Arg::isExtendedOffsetAddr):
(JSC::B3::Air::Arg::isMemory):
(JSC::B3::Air::Arg::base):
(JSC::B3::Air::Arg::offset):
(JSC::B3::Air::Arg::isGP):
(JSC::B3::Air::Arg::isFP):
(JSC::B3::Air::Arg::isValidForm):
(JSC::B3::Air::Arg::forEachTmpFast):
(JSC::B3::Air::Arg::forEachTmp):
(JSC::B3::Air::Arg::asAddress):
(JSC::B3::Air::Arg::stackAddr): Deleted.
* b3/air/AirCCallSpecial.cpp:
(JSC::B3::Air::CCallSpecial::isValid):
(JSC::B3::Air::CCallSpecial::admitsExtendedOffsetAddr):
(JSC::B3::Air::CCallSpecial::generate):
* b3/air/AirCCallSpecial.h:
* b3/air/AirCode.cpp:
(JSC::B3::Air::Code::Code):
(JSC::B3::Air::Code::pinRegister): Check that the register wasn't
pinned before pinning it. It's likely a bug to pin the same
register twice.
* b3/air/AirCustom.h:
(JSC::B3::Air::PatchCustom::admitsExtendedOffsetAddr):
(JSC::B3::Air::CCallCustom::admitsExtendedOffsetAddr):
(JSC::B3::Air::ShuffleCustom::admitsExtendedOffsetAddr):
(JSC::B3::Air::EntrySwitchCustom::admitsExtendedOffsetAddr):
(JSC::B3::Air::WasmBoundsCheckCustom::admitsExtendedOffsetAddr):
* b3/air/AirGenerate.cpp:
(JSC::B3::Air::generate):
* b3/air/AirInst.h:
* b3/air/AirInstInlines.h:
(JSC::B3::Air::Inst::admitsExtendedOffsetAddr):
* b3/air/AirLowerStackArgs.cpp:
(JSC::B3::Air::lowerStackArgs):
* b3/air/AirPrintSpecial.cpp:
(JSC::B3::Air::PrintSpecial::admitsExtendedOffsetAddr):
(JSC::B3::Air::PrintSpecial::generate):
* b3/air/AirPrintSpecial.h:
* b3/air/AirSpecial.h:
* b3/air/opcode_generator.rb:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (216305 => 216306)


--- trunk/Source/_javascript_Core/ChangeLog	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-05-06 03:57:42 UTC (rev 216306)
@@ -1,3 +1,123 @@
+2017-05-05  JF Bastien  <jfbast...@apple.com>
+
+        WebAssembly: Air::Inst::generate crashes on large binary on A64
+        https://bugs.webkit.org/show_bug.cgi?id=170215
+
+        Reviewed by Filip Pizlo.
+
+        ARM can't encode all offsets in a single instruction. We usualy
+        handle this type of detail early, or the macro assembler uses a
+        scratch register to take care of the large immediate. After
+        register allocation we assumed that we would never get large
+        offsets, and asserted this was the case. That was a fine
+        assumption with _javascript_, but WebAssembly ends up generating
+        stack frames which are too big to encode.
+
+        There are two places that needed to be fixed:
+            1. AirGenerate
+            2. AirLowerStackArgs
+
+        We now unconditionally pin the dataTempRegister on ARM64, and use
+        it when immediates don't fit.
+
+        Number 1. is easy: we're just incrementing SP, make sure we can
+        use a scratch register when that happens.
+
+        Number 2. is more complex: not all Inst can receive a stack
+        argument whose base register isn't SP or FP. Specifically,
+        Patchpoints and Stackmaps get very sad because they just want to
+        know the offset value, but when we materialize the offset as
+        follows:
+
+            Move (spill337), (spill201), %r0, @8735
+
+        Becomes (where %r16 is dataTempRegister):
+            Move $1404, %r16, @8736
+            Add64 %sp, %r16, @8736
+            Move (%r16), 2032(%sp), %r0, @8736
+
+        The code currently doesn't see through our little dance. To work
+        around this issue we introduce a new Air Arg kind:
+        ExtendedOffsetAddr. This is the same as a regular Addr, but with
+        an offset which may be too big to encode. Opcodes then declare
+        whether their arguments can handle such inputs, and if so we
+        generate them, otherwise we generate Addr as shown above.
+
+        None of this affects x86 because it can always encode large
+        immediates.
+
+        This patch also drive-by converts some uses of `override` to
+        `final`. It makes the code easier to grok, and maybe helps the
+        optimizer sometimes but really that doens't matter.
+
+        * assembler/MacroAssembler.h:
+        * assembler/MacroAssemblerARM64.h:
+        * b3/B3CheckSpecial.cpp:
+        (JSC::B3::CheckSpecial::admitsExtendedOffsetAddr):
+        * b3/B3CheckSpecial.h:
+        * b3/B3Common.cpp:
+        (JSC::B3::pinnedExtendedOffsetAddrRegister): keep the CPU-specific
+        pinning information in a cpp file
+        * b3/B3Common.h:
+        * b3/B3PatchpointSpecial.cpp:
+        (JSC::B3::PatchpointSpecial::admitsExtendedOffsetAddr):
+        * b3/B3PatchpointSpecial.h:
+        * b3/B3StackmapSpecial.cpp:
+        (JSC::B3::StackmapSpecial::isArgValidForRep):
+        (JSC::B3::StackmapSpecial::repForArg):
+        * b3/B3StackmapSpecial.h:
+        * b3/air/AirArg.cpp:
+        (JSC::B3::Air::Arg::isStackMemory):
+        (JSC::B3::Air::Arg::jsHash):
+        (JSC::B3::Air::Arg::dump):
+        (WTF::printInternal):
+        (JSC::B3::Air::Arg::stackAddrImpl): Deleted. There was only one
+        use of this (in AirLowerStackArgs) and it was now confusing to
+        split the logic up between these two. Inline the code that used to
+        be here into its one usepoint instead.
+        * b3/air/AirArg.h:
+        (JSC::B3::Air::Arg::extendedOffsetAddr):
+        (JSC::B3::Air::Arg::isExtendedOffsetAddr):
+        (JSC::B3::Air::Arg::isMemory):
+        (JSC::B3::Air::Arg::base):
+        (JSC::B3::Air::Arg::offset):
+        (JSC::B3::Air::Arg::isGP):
+        (JSC::B3::Air::Arg::isFP):
+        (JSC::B3::Air::Arg::isValidForm):
+        (JSC::B3::Air::Arg::forEachTmpFast):
+        (JSC::B3::Air::Arg::forEachTmp):
+        (JSC::B3::Air::Arg::asAddress):
+        (JSC::B3::Air::Arg::stackAddr): Deleted.
+        * b3/air/AirCCallSpecial.cpp:
+        (JSC::B3::Air::CCallSpecial::isValid):
+        (JSC::B3::Air::CCallSpecial::admitsExtendedOffsetAddr):
+        (JSC::B3::Air::CCallSpecial::generate):
+        * b3/air/AirCCallSpecial.h:
+        * b3/air/AirCode.cpp:
+        (JSC::B3::Air::Code::Code):
+        (JSC::B3::Air::Code::pinRegister): Check that the register wasn't
+        pinned before pinning it. It's likely a bug to pin the same
+        register twice.
+        * b3/air/AirCustom.h:
+        (JSC::B3::Air::PatchCustom::admitsExtendedOffsetAddr):
+        (JSC::B3::Air::CCallCustom::admitsExtendedOffsetAddr):
+        (JSC::B3::Air::ShuffleCustom::admitsExtendedOffsetAddr):
+        (JSC::B3::Air::EntrySwitchCustom::admitsExtendedOffsetAddr):
+        (JSC::B3::Air::WasmBoundsCheckCustom::admitsExtendedOffsetAddr):
+        * b3/air/AirGenerate.cpp:
+        (JSC::B3::Air::generate):
+        * b3/air/AirInst.h:
+        * b3/air/AirInstInlines.h:
+        (JSC::B3::Air::Inst::admitsExtendedOffsetAddr):
+        * b3/air/AirLowerStackArgs.cpp:
+        (JSC::B3::Air::lowerStackArgs):
+        * b3/air/AirPrintSpecial.cpp:
+        (JSC::B3::Air::PrintSpecial::admitsExtendedOffsetAddr):
+        (JSC::B3::Air::PrintSpecial::generate):
+        * b3/air/AirPrintSpecial.h:
+        * b3/air/AirSpecial.h:
+        * b3/air/opcode_generator.rb:
+
 2017-05-05  Oliver Hunt  <oli...@apple.com>
 
         Move trivial String prototype functions to JS builtins

Modified: trunk/Source/_javascript_Core/assembler/MacroAssembler.h (216305 => 216306)


--- trunk/Source/_javascript_Core/assembler/MacroAssembler.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/assembler/MacroAssembler.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -821,7 +821,7 @@
         return MacroAssemblerBase::branchTest8(cond, Address(address.base, address.offset), mask);
     }
 
-#else // !CPU(X86_64)
+#else // !CPU(X86_64) && !CPU(ARM64)
 
     void addPtr(RegisterID src, RegisterID dest)
     {

Modified: trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h (216305 => 216306)


--- trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/assembler/MacroAssemblerARM64.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -39,8 +39,8 @@
     static const unsigned numGPRs = 32;
     static const unsigned numFPRs = 32;
     
-    static const RegisterID dataTempRegister = ARM64Registers::ip0;
-    static const RegisterID memoryTempRegister = ARM64Registers::ip1;
+    static constexpr RegisterID dataTempRegister = ARM64Registers::ip0;
+    static constexpr RegisterID memoryTempRegister = ARM64Registers::ip1;
 
     RegisterID scratchRegister()
     {

Modified: trunk/Source/_javascript_Core/b3/B3CheckSpecial.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3CheckSpecial.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3CheckSpecial.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -140,6 +140,13 @@
     return admitsStackImpl(numB3Args(inst), m_numCheckArgs + 1, inst, argIndex);
 }
 
+bool CheckSpecial::admitsExtendedOffsetAddr(Inst& inst, unsigned argIndex)
+{
+    if (argIndex >= 1 && argIndex < 1 + m_numCheckArgs)
+        return false;
+    return admitsStack(inst, argIndex);
+}
+
 std::optional<unsigned> CheckSpecial::shouldTryAliasingDef(Inst& inst)
 {
     if (std::optional<unsigned> branchDef = hiddenBranch(inst).shouldTryAliasingDef())

Modified: trunk/Source/_javascript_Core/b3/B3CheckSpecial.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3CheckSpecial.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3CheckSpecial.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -120,18 +120,19 @@
     // Constructs and returns the Inst representing the branch that this will use.
     Air::Inst hiddenBranch(const Air::Inst&) const;
 
-    void forEachArg(Air::Inst&, const ScopedLambda<Air::Inst::EachArgCallback>&) override;
-    bool isValid(Air::Inst&) override;
-    bool admitsStack(Air::Inst&, unsigned argIndex) override;
-    std::optional<unsigned> shouldTryAliasingDef(Air::Inst&) override;
+    void forEachArg(Air::Inst&, const ScopedLambda<Air::Inst::EachArgCallback>&) final;
+    bool isValid(Air::Inst&) final;
+    bool admitsStack(Air::Inst&, unsigned argIndex) final;
+    bool admitsExtendedOffsetAddr(Air::Inst&, unsigned) final;
+    std::optional<unsigned> shouldTryAliasingDef(Air::Inst&) final;
 
     // NOTE: the generate method will generate the hidden branch and then register a LatePath that
     // generates the stackmap. Super crazy dude!
 
-    CCallHelpers::Jump generate(Air::Inst&, CCallHelpers&, Air::GenerationContext&) override;
+    CCallHelpers::Jump generate(Air::Inst&, CCallHelpers&, Air::GenerationContext&) final;
 
-    void dumpImpl(PrintStream&) const override;
-    void deepDumpImpl(PrintStream&) const override;
+    void dumpImpl(PrintStream&) const final;
+    void deepDumpImpl(PrintStream&) const final;
 
 private:
     Air::Kind m_checkKind;

Modified: trunk/Source/_javascript_Core/b3/B3Common.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3Common.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3Common.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -70,6 +70,17 @@
     return Options::logB3PhaseTimes();
 }
 
+std::optional<GPRReg> pinnedExtendedOffsetAddrRegister()
+{
+#if CPU(ARM64)
+    return static_cast<GPRReg>(+MacroAssembler::dataTempRegister);
+#elif CPU(X86_64)
+    return std::nullopt;
+#else
+#error Unhandled architecture.
+#endif
+}
+
 } } // namespace JSC::B3
 
 #endif // ENABLE(B3_JIT)

Modified: trunk/Source/_javascript_Core/b3/B3Common.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3Common.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3Common.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -28,6 +28,7 @@
 #if ENABLE(B3_JIT)
 
 #include "CPU.h"
+#include "GPRInfo.h"
 #include "JSExportMacros.h"
 #include "Options.h"
 #include <wtf/Optional.h>
@@ -182,6 +183,8 @@
     return Options::defaultB3OptLevel();
 }
 
+std::optional<GPRReg> pinnedExtendedOffsetAddrRegister();
+
 } } // namespace JSC::B3
 
 #endif // ENABLE(B3_JIT)

Modified: trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -130,6 +130,11 @@
     return admitsStackImpl(0, 2, inst, argIndex);
 }
 
+bool PatchpointSpecial::admitsExtendedOffsetAddr(Inst& inst, unsigned argIndex)
+{
+    return admitsStack(inst, argIndex);
+}
+
 CCallHelpers::Jump PatchpointSpecial::generate(
     Inst& inst, CCallHelpers& jit, GenerationContext& context)
 {

Modified: trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3PatchpointSpecial.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -47,19 +47,20 @@
     virtual ~PatchpointSpecial();
 
 protected:
-    void forEachArg(Air::Inst&, const ScopedLambda<Air::Inst::EachArgCallback>&) override;
-    bool isValid(Air::Inst&) override;
-    bool admitsStack(Air::Inst&, unsigned argIndex) override;
+    void forEachArg(Air::Inst&, const ScopedLambda<Air::Inst::EachArgCallback>&) final;
+    bool isValid(Air::Inst&) final;
+    bool admitsStack(Air::Inst&, unsigned argIndex) final;
+    bool admitsExtendedOffsetAddr(Air::Inst&, unsigned) final;
 
     // NOTE: the generate method will generate the hidden branch and then register a LatePath that
     // generates the stackmap. Super crazy dude!
 
-    CCallHelpers::Jump generate(Air::Inst&, CCallHelpers&, Air::GenerationContext&) override;
+    CCallHelpers::Jump generate(Air::Inst&, CCallHelpers&, Air::GenerationContext&) final;
     
-    bool isTerminal(Air::Inst&) override;
+    bool isTerminal(Air::Inst&) final;
 
-    void dumpImpl(PrintStream&) const override;
-    void deepDumpImpl(PrintStream&) const override;
+    void dumpImpl(PrintStream&) const final;
+    void deepDumpImpl(PrintStream&) const final;
 };
 
 } } // namespace JSC::B3

Modified: trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3StackmapSpecial.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -252,7 +252,7 @@
     case ValueRep::StackArgument:
         if (arg == Arg::callArg(rep.offsetFromSP()))
             return true;
-        if (arg.isAddr() && code.frameSize()) {
+        if ((arg.isAddr() || arg.isExtendedOffsetAddr()) && code.frameSize()) {
             if (arg.base() == Tmp(GPRInfo::callFrameRegister)
                 && arg.offset() == rep.offsetFromSP() - code.frameSize())
                 return true;
@@ -277,6 +277,9 @@
     case Arg::BigImm:
         return ValueRep::constant(arg.value());
         break;
+    case Arg::ExtendedOffsetAddr:
+        ASSERT(arg.base() == Tmp(GPRInfo::callFrameRegister));
+        FALLTHROUGH;
     case Arg::Addr:
         if (arg.base() == Tmp(GPRInfo::callFrameRegister))
             return ValueRep::stack(arg.offset());

Modified: trunk/Source/_javascript_Core/b3/B3StackmapSpecial.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/B3StackmapSpecial.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/B3StackmapSpecial.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -51,9 +51,9 @@
     };
 
 protected:
-    void reportUsedRegisters(Air::Inst&, const RegisterSet&) override;
-    RegisterSet extraEarlyClobberedRegs(Air::Inst&) override;
-    RegisterSet extraClobberedRegs(Air::Inst&) override;
+    void reportUsedRegisters(Air::Inst&, const RegisterSet&) final;
+    RegisterSet extraEarlyClobberedRegs(Air::Inst&) final;
+    RegisterSet extraClobberedRegs(Air::Inst&) final;
 
     // Note that this does not override generate() or dumpImpl()/deepDumpImpl(). We have many some
     // subclasses that implement that.

Modified: trunk/Source/_javascript_Core/b3/air/AirArg.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirArg.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -41,19 +41,6 @@
 
 namespace JSC { namespace B3 { namespace Air {
 
-Arg Arg::stackAddrImpl(int32_t offsetFromFP, unsigned frameSize, Width width)
-{
-    Arg result = Arg::addr(Air::Tmp(GPRInfo::callFrameRegister), offsetFromFP);
-    if (!result.isValidForm(width)) {
-        result = Arg::addr(
-            Air::Tmp(MacroAssembler::stackPointerRegister),
-            offsetFromFP + frameSize);
-        if (!result.isValidForm(width))
-            result = Arg();
-    }
-    return result;
-}
-
 bool Arg::isStackMemory() const
 {
     switch (kind()) {
@@ -60,6 +47,7 @@
     case Addr:
         return base() == Air::Tmp(GPRInfo::callFrameRegister)
             || base() == Air::Tmp(MacroAssembler::stackPointerRegister);
+    case ExtendedOffsetAddr:
     case Stack:
     case CallArg:
         return true;
@@ -128,6 +116,7 @@
         result += m_base.internalValue();
         break;
     case Addr:
+    case ExtendedOffsetAddr:
         result += m_offset;
         result += m_base.internalValue();
         break;
@@ -171,6 +160,7 @@
         out.print("(", base(), ")");
         return;
     case Addr:
+    case ExtendedOffsetAddr:
         if (offset())
             out.print(offset());
         out.print("(", base(), ")");
@@ -249,6 +239,9 @@
     case Arg::Addr:
         out.print("Addr");
         return;
+    case Arg::ExtendedOffsetAddr:
+        out.print("ExtendedOffsetAddr");
+        return;
     case Arg::Stack:
         out.print("Stack");
         return;

Modified: trunk/Source/_javascript_Core/b3/air/AirArg.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirArg.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirArg.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -75,6 +75,7 @@
         // (UseAddr) addresses.
         SimpleAddr,
         Addr,
+        ExtendedOffsetAddr,
         Stack,
         CallArg,
         Index,
@@ -546,6 +547,16 @@
         return result;
     }
 
+    template<typename Int, typename = Value::IsLegalOffset<Int>>
+    static Arg extendedOffsetAddr(Int offsetFromFP)
+    {
+        Arg result;
+        result.m_kind = ExtendedOffsetAddr;
+        result.m_base = Air::Tmp(MacroAssembler::framePointerRegister);
+        result.m_offset = offsetFromFP;
+        return result;
+    }
+
     static Arg addr(Air::Tmp base)
     {
         return addr(base, 0);
@@ -575,12 +586,6 @@
         return result;
     }
 
-    template<typename Int, typename = Value::IsLegalOffset<Int>>
-    static Arg stackAddr(Int offsetFromFP, unsigned frameSize, Width width)
-    {
-        return stackAddrImpl(offsetFromFP, frameSize, width);
-    }
-
     // If you don't pass a Width, this optimistically assumes that you're using the right width.
     static bool isValidScale(unsigned scale, std::optional<Width> width = std::nullopt)
     {
@@ -759,6 +764,11 @@
         return kind() == Addr;
     }
 
+    bool isExtendedOffsetAddr() const
+    {
+        return kind() == ExtendedOffsetAddr;
+    }
+
     bool isStack() const
     {
         return kind() == Stack;
@@ -779,6 +789,7 @@
         switch (kind()) {
         case SimpleAddr:
         case Addr:
+        case ExtendedOffsetAddr:
         case Stack:
         case CallArg:
         case Index:
@@ -792,6 +803,7 @@
     // stack references. The following idioms are recognized:
     // - the Stack kind
     // - the CallArg kind
+    // - the ExtendedOffsetAddr kind
     // - the Addr kind with the base being either SP or FP
     // Callers of this function are allowed to expect that if it returns true, then it must be one of
     // these easy-to-recognize kinds. So, making this function recognize more kinds could break things.
@@ -943,7 +955,7 @@
 
     Air::Tmp base() const
     {
-        ASSERT(kind() == SimpleAddr || kind() == Addr || kind() == Index);
+        ASSERT(kind() == SimpleAddr || kind() == Addr || kind() == ExtendedOffsetAddr || kind() == Index);
         return m_base;
     }
 
@@ -953,7 +965,7 @@
     {
         if (kind() == Stack)
             return static_cast<Value::OffsetType>(m_scale);
-        ASSERT(kind() == Addr || kind() == CallArg || kind() == Index);
+        ASSERT(kind() == Addr || kind() == ExtendedOffsetAddr || kind() == CallArg || kind() == Index);
         return static_cast<Value::OffsetType>(m_offset);
     }
 
@@ -1012,6 +1024,7 @@
         case BitImm64:
         case SimpleAddr:
         case Addr:
+        case ExtendedOffsetAddr:
         case Index:
         case Stack:
         case CallArg:
@@ -1047,6 +1060,7 @@
             return false;
         case SimpleAddr:
         case Addr:
+        case ExtendedOffsetAddr:
         case Index:
         case Stack:
         case CallArg:
@@ -1222,6 +1236,7 @@
         case BitImm64:
             return isValidBitImm64Form(value());
         case SimpleAddr:
+        case ExtendedOffsetAddr:
             return true;
         case Addr:
         case Stack:
@@ -1247,6 +1262,7 @@
         case Tmp:
         case SimpleAddr:
         case Addr:
+        case ExtendedOffsetAddr:
             functor(m_base);
             break;
         case Index:
@@ -1288,6 +1304,7 @@
             break;
         case SimpleAddr:
         case Addr:
+        case ExtendedOffsetAddr:
             functor(m_base, Use, GP, argRole == UseAddr ? argWidth : pointerWidth());
             break;
         case Index:
@@ -1326,7 +1343,7 @@
     {
         if (isSimpleAddr())
             return MacroAssembler::Address(m_base.gpr());
-        ASSERT(isAddr());
+        ASSERT(isAddr() || isExtendedOffsetAddr());
         return MacroAssembler::Address(m_base.gpr(), static_cast<Value::OffsetType>(m_offset));
     }
 
@@ -1438,8 +1455,6 @@
     }
 
 private:
-    static Arg stackAddrImpl(int32_t, unsigned, Width);
-
     int64_t m_offset { 0 };
     Kind m_kind { Invalid };
     int32_t m_scale { 1 };

Modified: trunk/Source/_javascript_Core/b3/air/AirCCallSpecial.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirCCallSpecial.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirCCallSpecial.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -82,6 +82,7 @@
             return false;
         case Arg::Tmp:
         case Arg::Addr:
+        case Arg::ExtendedOffsetAddr:
         case Arg::Stack:
         case Arg::CallArg:
             break;
@@ -118,6 +119,11 @@
     return false;
 }
 
+bool CCallSpecial::admitsExtendedOffsetAddr(Inst& inst, unsigned argIndex)
+{
+    return admitsStack(inst, argIndex);
+}
+
 void CCallSpecial::reportUsedRegisters(Inst&, const RegisterSet&)
 {
 }
@@ -134,6 +140,7 @@
         jit.call(inst.args[calleeArgOffset].gpr());
         break;
     case Arg::Addr:
+    case Arg::ExtendedOffsetAddr:
         jit.call(inst.args[calleeArgOffset].asAddress());
         break;
     default:

Modified: trunk/Source/_javascript_Core/b3/air/AirCCallSpecial.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirCCallSpecial.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirCCallSpecial.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -52,16 +52,17 @@
     static const GPRReg scratchRegister = GPRInfo::nonArgGPR0;
 
 protected:
-    void forEachArg(Inst&, const ScopedLambda<Inst::EachArgCallback>&) override;
-    bool isValid(Inst&) override;
-    bool admitsStack(Inst&, unsigned argIndex) override;
-    void reportUsedRegisters(Inst&, const RegisterSet&) override;
-    CCallHelpers::Jump generate(Inst&, CCallHelpers&, GenerationContext&) override;
-    RegisterSet extraEarlyClobberedRegs(Inst&) override;
-    RegisterSet extraClobberedRegs(Inst&) override;
+    void forEachArg(Inst&, const ScopedLambda<Inst::EachArgCallback>&) final;
+    bool isValid(Inst&) final;
+    bool admitsStack(Inst&, unsigned argIndex) final;
+    bool admitsExtendedOffsetAddr(Inst&, unsigned) final;
+    void reportUsedRegisters(Inst&, const RegisterSet&) final;
+    CCallHelpers::Jump generate(Inst&, CCallHelpers&, GenerationContext&) final;
+    RegisterSet extraEarlyClobberedRegs(Inst&) final;
+    RegisterSet extraClobberedRegs(Inst&) final;
 
-    void dumpImpl(PrintStream&) const override;
-    void deepDumpImpl(PrintStream&) const override;
+    void dumpImpl(PrintStream&) const final;
+    void deepDumpImpl(PrintStream&) const final;
 
 private:
     static const unsigned specialArgOffset = 0;

Modified: trunk/Source/_javascript_Core/b3/air/AirCode.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirCode.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirCode.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -62,6 +62,9 @@
                 });
             setRegsInPriorityOrder(bank, result);
         });
+
+    if (auto reg = pinnedExtendedOffsetAddrRegister())
+        pinRegister(*reg);
 }
 
 Code::~Code()
@@ -82,6 +85,7 @@
 void Code::pinRegister(Reg reg)
 {
     Vector<Reg>& regs = regsInPriorityOrderImpl(Arg(Tmp(reg)).bank());
+    ASSERT(regs.contains(reg));
     regs.removeFirst(reg);
     m_mutableRegs.clear(reg);
     ASSERT(!regs.contains(reg));

Modified: trunk/Source/_javascript_Core/b3/air/AirCustom.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirCustom.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirCustom.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -81,6 +81,13 @@
         return inst.args[0].special()->admitsStack(inst, argIndex);
     }
 
+    static bool admitsExtendedOffsetAddr(Inst& inst, unsigned argIndex)
+    {
+        if (!argIndex)
+            return false;
+        return inst.args[0].special()->admitsExtendedOffsetAddr(inst, argIndex);
+    }
+
     static std::optional<unsigned> shouldTryAliasingDef(Inst& inst)
     {
         return inst.args[0].special()->shouldTryAliasingDef(inst);
@@ -157,6 +164,11 @@
     {
         return true;
     }
+
+    static bool admitsExtendedOffsetAddr(Inst&, unsigned)
+    {
+        return false;
+    }
     
     static bool isTerminal(Inst&)
     {
@@ -221,6 +233,11 @@
         }
     }
 
+    static bool admitsExtendedOffsetAddr(Inst&, unsigned)
+    {
+        return false;
+    }
+
     static bool isTerminal(Inst&)
     {
         return false;
@@ -255,6 +272,11 @@
     {
         return false;
     }
+
+    static bool admitsExtendedOffsetAddr(Inst&, unsigned)
+    {
+        return false;
+    }
     
     static bool isTerminal(Inst&)
     {
@@ -296,6 +318,11 @@
         return false;
     }
 
+    static bool admitsExtendedOffsetAddr(Inst&, unsigned)
+    {
+        return false;
+    }
+
     static bool isTerminal(Inst&)
     {
         return false;

Modified: trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirGenerate.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -46,6 +46,7 @@
 #include "AirReportUsedRegisters.h"
 #include "AirSimplifyCFG.h"
 #include "AirValidate.h"
+#include "AllowMacroScratchRegisterUsageIf.h"
 #include "B3Common.h"
 #include "B3Procedure.h"
 #include "B3TimingScope.h"
@@ -216,8 +217,10 @@
                 disassembler->startEntrypoint(jit); 
 
             jit.emitFunctionPrologue();
-            if (code.frameSize())
+            if (code.frameSize()) {
+                AllowMacroScratchRegisterUsageIf allowScratch(jit, isARM64());
                 jit.addPtr(CCallHelpers::TrustedImm32(-code.frameSize()), MacroAssembler::stackPointerRegister);
+            }
             
             for (const RegisterAtOffset& entry : code.calleeSaveRegisterAtOffsetList()) {
                 if (entry.reg().isGPR())

Modified: trunk/Source/_javascript_Core/b3/air/AirInst.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirInst.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirInst.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -167,7 +167,10 @@
     // This function is auto-generated by opcode_generator.rb.
     bool admitsStack(unsigned argIndex);
     bool admitsStack(Arg&);
-    
+
+    bool admitsExtendedOffsetAddr(unsigned argIndex);
+    bool admitsExtendedOffsetAddr(Arg&);
+
     // Defined by opcode_generator.rb.
     bool isTerminal();
 

Modified: trunk/Source/_javascript_Core/b3/air/AirInstInlines.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirInstInlines.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirInstInlines.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -111,6 +111,11 @@
     return admitsStack(&arg - &args[0]);
 }
 
+inline bool Inst::admitsExtendedOffsetAddr(Arg& arg)
+{
+    return admitsExtendedOffsetAddr(&arg - &args[0]);
+}
+
 inline std::optional<unsigned> Inst::shouldTryAliasingDef()
 {
     if (!isX86())

Modified: trunk/Source/_javascript_Core/b3/air/AirLowerStackArgs.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirLowerStackArgs.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirLowerStackArgs.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -63,27 +63,49 @@
     // transformation since we can search the StackSlots array to figure out which StackSlot any
     // offset-from-FP refers to.
 
-    // FIXME: This may produce addresses that aren't valid if we end up with a ginormous stack frame.
-    // We would have to scavenge for temporaries if this happened. Fortunately, this case will be
-    // extremely rare so we can do crazy things when it arises.
-    // https://bugs.webkit.org/show_bug.cgi?id=152530
-
     InsertionSet insertionSet(code);
     for (BasicBlock* block : code) {
+        // FIXME We can keep track of the last large offset which was materialized in this block, and reuse the register
+        // if it hasn't been clobbered instead of renetating imm+add+addr every time. https://bugs.webkit.org/show_bug.cgi?id=171387
+
         for (unsigned instIndex = 0; instIndex < block->size(); ++instIndex) {
             Inst& inst = block->at(instIndex);
+            bool isPatch = inst.kind.opcode == Patch;
+
             inst.forEachArg(
                 [&] (Arg& arg, Arg::Role role, Bank, Width width) {
-                    auto stackAddr = [&] (Value::OffsetType offset) -> Arg {
-                        Arg result = Arg::stackAddr(offset, code.frameSize(), width);
-                        if (!result) {
-                            dataLog("FATAL: Could not create stack reference for offset = ", offset, " and width = ", width, "\n");
-                            dataLog("Code:\n");
-                            dataLog(code);
-                            dataLog("FATAL: Could not create stack reference for offset = ", offset, " and width = ", width, "\n");
-                            RELEASE_ASSERT_NOT_REACHED();
+                    auto stackAddr = [&] (Value::OffsetType offsetFromFP) -> Arg {
+                        int32_t offsetFromSP = offsetFromFP + code.frameSize();
+
+                        if (isPatch && inst.admitsExtendedOffsetAddr(arg)) {
+                            // Stackmaps and patchpoints expect addr inputs relative to SP or FP only. We might as well
+                            // not even bother generating an addr with valid form for these opcodes since extended offset
+                            // addr is always valid.
+                            return Arg::extendedOffsetAddr(offsetFromFP);
                         }
+
+                        Arg result = Arg::addr(Air::Tmp(GPRInfo::callFrameRegister), offsetFromFP);
+                        if (result.isValidForm(width))
+                            return result;
+
+                        result = Arg::addr(Air::Tmp(MacroAssembler::stackPointerRegister), offsetFromSP);
+                        if (result.isValidForm(width))
+                            return result;
+#if CPU(ARM64)
+                        ASSERT(pinnedExtendedOffsetAddrRegister());
+                        Air::Tmp tmp = Air::Tmp(*pinnedExtendedOffsetAddrRegister());
+
+                        Arg largeOffset = Arg::isValidImmForm(offsetFromSP) ? Arg::imm(offsetFromSP) : Arg::bigImm(offsetFromSP);
+                        insertionSet.insert(instIndex, Move, inst.origin, largeOffset, tmp);
+                        insertionSet.insert(instIndex, Add64, inst.origin, Air::Tmp(MacroAssembler::stackPointerRegister), tmp);
+                        result = Arg::addr(tmp, 0);
                         return result;
+#elif CPU(X86_64)
+                        // Can't happen on x86: immediates are always big enough for frame size.
+                        RELEASE_ASSERT_NOT_REACHED();
+#else
+#error Unhandled architecture.
+#endif
                     };
                     
                     switch (arg.kind()) {

Modified: trunk/Source/_javascript_Core/b3/air/AirPrintSpecial.cpp (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirPrintSpecial.cpp	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirPrintSpecial.cpp	2017-05-06 03:57:42 UTC (rev 216306)
@@ -56,6 +56,11 @@
     return false;
 }
 
+bool PrintSpecial::admitsExtendedOffsetAddr(Inst&, unsigned)
+{
+    return false;
+}
+
 void PrintSpecial::reportUsedRegisters(Inst&, const RegisterSet&)
 {
 }
@@ -71,6 +76,7 @@
                 term = Printer::Printer<MacroAssembler::RegisterID>(arg.gpr());
                 break;
             case Arg::Addr:
+            case Arg::ExtendedOffsetAddr:
                 term = Printer::Printer<MacroAssembler::Address>(arg.asAddress());
                 break;
             default:

Modified: trunk/Source/_javascript_Core/b3/air/AirPrintSpecial.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirPrintSpecial.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirPrintSpecial.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -100,16 +100,17 @@
     static const GPRReg scratchRegister = GPRInfo::nonArgGPR0;
     
 protected:
-    void forEachArg(Inst&, const ScopedLambda<Inst::EachArgCallback>&) override;
-    bool isValid(Inst&) override;
-    bool admitsStack(Inst&, unsigned argIndex) override;
-    void reportUsedRegisters(Inst&, const RegisterSet&) override;
-    CCallHelpers::Jump generate(Inst&, CCallHelpers&, GenerationContext&) override;
-    RegisterSet extraEarlyClobberedRegs(Inst&) override;
-    RegisterSet extraClobberedRegs(Inst&) override;
+    void forEachArg(Inst&, const ScopedLambda<Inst::EachArgCallback>&) final;
+    bool isValid(Inst&) final;
+    bool admitsStack(Inst&, unsigned argIndex) final;
+    bool admitsExtendedOffsetAddr(Inst&, unsigned) final;
+    void reportUsedRegisters(Inst&, const RegisterSet&) final;
+    CCallHelpers::Jump generate(Inst&, CCallHelpers&, GenerationContext&) final;
+    RegisterSet extraEarlyClobberedRegs(Inst&) final;
+    RegisterSet extraClobberedRegs(Inst&) final;
     
-    void dumpImpl(PrintStream&) const override;
-    void deepDumpImpl(PrintStream&) const override;
+    void dumpImpl(PrintStream&) const final;
+    void deepDumpImpl(PrintStream&) const final;
     
 private:
     static const unsigned specialArgOffset = 0;

Modified: trunk/Source/_javascript_Core/b3/air/AirSpecial.h (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/AirSpecial.h	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/AirSpecial.h	2017-05-06 03:57:42 UTC (rev 216306)
@@ -55,6 +55,7 @@
     virtual void forEachArg(Inst&, const ScopedLambda<Inst::EachArgCallback>&) = 0;
     virtual bool isValid(Inst&) = 0;
     virtual bool admitsStack(Inst&, unsigned argIndex) = 0;
+    virtual bool admitsExtendedOffsetAddr(Inst&, unsigned argIndex) = 0;
     virtual std::optional<unsigned> shouldTryAliasingDef(Inst&);
 
     // This gets called on for each Inst that uses this Special. Note that there is no way to

Modified: trunk/Source/_javascript_Core/b3/air/opcode_generator.rb (216305 => 216306)


--- trunk/Source/_javascript_Core/b3/air/opcode_generator.rb	2017-05-06 03:27:16 UTC (rev 216305)
+++ trunk/Source/_javascript_Core/b3/air/opcode_generator.rb	2017-05-06 03:57:42 UTC (rev 216306)
@@ -229,7 +229,7 @@
 end
 
 def isKind(token)
-    token =~ /\A((Tmp)|(Imm)|(BigImm)|(BitImm)|(BitImm64)|(SimpleAddr)|(Addr)|(Index)|(RelCond)|(ResCond)|(DoubleCond)|(StatusCond))\Z/
+    token =~ /\A((Tmp)|(Imm)|(BigImm)|(BitImm)|(BitImm64)|(SimpleAddr)|(Addr)|(ExtendedOffsetAddr)|(Index)|(RelCond)|(ResCond)|(DoubleCond)|(StatusCond))\Z/
 end
 
 def isArch(token)
@@ -303,7 +303,7 @@
 
     def consumeKind
         result = token.string
-        parseError("Expected kind (Imm, BigImm, BitImm, BitImm64, Tmp, SimpleAddr, Addr, Index, RelCond, ResCond, DoubleCond, or StatusCond)") unless isKind(result)
+        parseError("Expected kind (Imm, BigImm, BitImm, BitImm64, Tmp, SimpleAddr, Addr, ExtendedOffsetAddr, Index, RelCond, ResCond, DoubleCond, or StatusCond)") unless isKind(result)
         advance
         result
     end
@@ -908,9 +908,13 @@
                         outp.puts "if (args[#{index}].isStack() && args[#{index}].stackSlot()->isSpill())"
                         outp.puts "OPGEN_RETURN(false);"
                     end
-                    
                     outp.puts "if (!Arg::isValidAddrForm(args[#{index}].offset()))"
                     outp.puts "OPGEN_RETURN(false);"
+                when "ExtendedOffsetAddr"
+                    if arg.role == "UA"
+                        outp.puts "if (args[#{index}].isStack() && args[#{index}].stackSlot()->isSpill())"
+                        outp.puts "OPGEN_RETURN(false);"
+                    end
                 when "Index"
                     outp.puts "if (!Arg::isValidIndexForm(args[#{index}].scale(), args[#{index}].offset(), #{arg.widthCode}))"
                     outp.puts "OPGEN_RETURN(false);"
@@ -1076,6 +1080,24 @@
     outp.puts "return false;"
     outp.puts "}"
 
+    outp.puts "bool Inst::admitsExtendedOffsetAddr(unsigned argIndex)"
+    outp.puts "{"
+    outp.puts "switch (kind.opcode) {"
+    $opcodes.values.each {
+        | opcode |
+        if opcode.custom
+            outp.puts "case Opcode::#{opcode.name}:"
+            outp.puts "OPGEN_RETURN(#{opcode.name}Custom::admitsExtendedOffsetAddr(*this, argIndex));"
+            outp.puts "break;"
+        end
+    }
+    outp.puts "default:"
+    outp.puts "break;"
+    outp.puts "}"
+    outp.puts "return false;"
+    outp.puts "}"
+
+
     outp.puts "bool Inst::isTerminal()"
     outp.puts "{"
     outp.puts "switch (kind.opcode) {"
@@ -1195,7 +1217,7 @@
                     outp.print "args[#{index}].asTrustedImm32()"
                 when "BigImm", "BitImm64"
                     outp.print "args[#{index}].asTrustedImm64()"
-                when "SimpleAddr", "Addr"
+                when "SimpleAddr", "Addr", "ExtendedOffsetAddr"
                     outp.print "args[#{index}].asAddress()"
                 when "Index"
                     outp.print "args[#{index}].asBaseIndex()"
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to