Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (214826 => 214827)
--- trunk/Source/_javascript_Core/ChangeLog 2017-04-03 18:44:05 UTC (rev 214826)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-04-03 18:55:34 UTC (rev 214827)
@@ -1,3 +1,36 @@
+2017-04-03 Filip Pizlo <fpi...@apple.com>
+
+ Inst::forEachArg could compile to more compact code
+ https://bugs.webkit.org/show_bug.cgi?id=170406
+
+ Reviewed by Sam Weinig.
+
+ Prior to this change, Inst::forEachArg compiled to a ginormous ALWAYS_INLINE switch statement.
+ It had one case for each opcode, and then each of those cases would have a switch statement over
+ the number of operands. Then the cases of that switch statement would have a sequence of calls to
+ the passed lambda. This meant that every user of forEachArg would generate an insane amount of
+ code. It also meant that the inlining achieved nothing, since the lambda would surely then not
+ be inlined - and if it was, then the icache pressure due to code bloat would surely negate any
+ benefits.
+
+ This replaces that code with a loop over a compact look-up table. We use the opcode and number of
+ operands as keys into that look-up table. The table only takes about 20KB. It has one byte for
+ each argument in each overload of each opcode.
+
+ I can't measure any reproducible change in performance, but the _javascript_Core framework binary
+ shrinks by 2.7 MB. This is a 15% reduction in _javascript_Core binary size.
+
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ * b3/B3Width.h:
+ * b3/air/AirCustom.h:
+ (JSC::B3::Air::PatchCustom::forEachArg):
+ * b3/air/AirFormTable.h: Added.
+ (JSC::B3::Air::decodeFormRole):
+ (JSC::B3::Air::decodeFormBank):
+ (JSC::B3::Air::decodeFormWidth):
+ * b3/air/AirInst.h:
+ * b3/air/opcode_generator.rb:
+
2017-04-03 Keith Miller <keith_mil...@apple.com>
WebAssembly: remove lastAllocatedMode from Memory
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (214826 => 214827)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2017-04-03 18:44:05 UTC (rev 214826)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2017-04-03 18:55:34 UTC (rev 214827)
@@ -189,6 +189,7 @@
0F2AC56B1E8A0BD50001EE3F /* AirAllocateRegistersByLinearScan.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2AC5691E8A0BD10001EE3F /* AirAllocateRegistersByLinearScan.h */; };
0F2AC56E1E8D7B000001EE3F /* AirPhaseInsertionSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2AC56C1E8D7AFF0001EE3F /* AirPhaseInsertionSet.cpp */; };
0F2AC56F1E8D7B030001EE3F /* AirPhaseInsertionSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2AC56D1E8D7AFF0001EE3F /* AirPhaseInsertionSet.h */; };
+ 0F2AC5711E8EE4540001EE3F /* AirFormTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2AC5701E8EE4520001EE3F /* AirFormTable.h */; };
0F2B66AC17B6B53F00A7AE3F /* GCIncomingRefCounted.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B66A817B6B53D00A7AE3F /* GCIncomingRefCounted.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F2B66AD17B6B54500A7AE3F /* GCIncomingRefCountedInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B66A917B6B53D00A7AE3F /* GCIncomingRefCountedInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F2B66AE17B6B54500A7AE3F /* GCIncomingRefCountedSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B66AA17B6B53D00A7AE3F /* GCIncomingRefCountedSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -2723,6 +2724,7 @@
0F2AC5691E8A0BD10001EE3F /* AirAllocateRegistersByLinearScan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirAllocateRegistersByLinearScan.h; path = b3/air/AirAllocateRegistersByLinearScan.h; sourceTree = "<group>"; };
0F2AC56C1E8D7AFF0001EE3F /* AirPhaseInsertionSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirPhaseInsertionSet.cpp; path = b3/air/AirPhaseInsertionSet.cpp; sourceTree = "<group>"; };
0F2AC56D1E8D7AFF0001EE3F /* AirPhaseInsertionSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirPhaseInsertionSet.h; path = b3/air/AirPhaseInsertionSet.h; sourceTree = "<group>"; };
+ 0F2AC5701E8EE4520001EE3F /* AirFormTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirFormTable.h; path = b3/air/AirFormTable.h; sourceTree = "<group>"; };
0F2B66A817B6B53D00A7AE3F /* GCIncomingRefCounted.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCIncomingRefCounted.h; sourceTree = "<group>"; };
0F2B66A917B6B53D00A7AE3F /* GCIncomingRefCountedInlines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCIncomingRefCountedInlines.h; sourceTree = "<group>"; };
0F2B66AA17B6B53D00A7AE3F /* GCIncomingRefCountedSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCIncomingRefCountedSet.h; sourceTree = "<group>"; };
@@ -5592,6 +5594,7 @@
262D85B51C0D650F006ACB61 /* AirFixPartialRegisterStalls.h */,
0F2AC5641E8A0B760001EE3F /* AirFixSpillsAfterTerminals.cpp */,
0F2AC5651E8A0B760001EE3F /* AirFixSpillsAfterTerminals.h */,
+ 0F2AC5701E8EE4520001EE3F /* AirFormTable.h */,
0FEC85521BDACDC70080FF74 /* AirFrequentedBlock.h */,
0FEC85531BDACDC70080FF74 /* AirGenerate.cpp */,
0FEC85541BDACDC70080FF74 /* AirGenerate.h */,
@@ -9336,6 +9339,7 @@
0F2B670B17B6B5AB00A7AE3F /* TypedArrayType.h in Headers */,
0FB7F39D15ED8E4600F167B2 /* TypeError.h in Headers */,
0F2D4DEA19832DAC007D4B19 /* TypeLocation.h in Headers */,
+ 0F2AC5711E8EE4540001EE3F /* AirFormTable.h in Headers */,
52B311011975B4670080857C /* TypeLocationCache.h in Headers */,
0FFB6C391AF48DDC00DB1BF7 /* TypeofType.h in Headers */,
52C952B719A289850069B386 /* TypeProfiler.h in Headers */,
Modified: trunk/Source/_javascript_Core/b3/B3Width.h (214826 => 214827)
--- trunk/Source/_javascript_Core/b3/B3Width.h 2017-04-03 18:44:05 UTC (rev 214826)
+++ trunk/Source/_javascript_Core/b3/B3Width.h 2017-04-03 18:55:34 UTC (rev 214827)
@@ -51,6 +51,13 @@
return Width32;
}
+// Don't use this unless the compiler forces you to.
+#if CPU(X86_64) || CPU(ARM64)
+#define POINTER_WIDTH Width64
+#else
+#define POINTER_WIDTH Width32
+#endif
+
inline Width widthForType(Type type)
{
switch (type) {
Modified: trunk/Source/_javascript_Core/b3/air/AirCustom.h (214826 => 214827)
--- trunk/Source/_javascript_Core/b3/air/AirCustom.h 2017-04-03 18:44:05 UTC (rev 214826)
+++ trunk/Source/_javascript_Core/b3/air/AirCustom.h 2017-04-03 18:55:34 UTC (rev 214827)
@@ -57,14 +57,13 @@
// Definition of Patch instruction. Patch is used to delegate the behavior of the instruction to the
// Special object, which will be the first argument to the instruction.
struct PatchCustom {
- template<typename Functor>
- static void forEachArg(Inst& inst, const Functor& functor)
+ static void forEachArg(Inst& inst, ScopedLambda<Inst::EachArgCallback> lambda)
{
// This is basically bogus, but it works for analyses that model Special as an
// immediate.
- functor(inst.args[0], Arg::Use, GP, pointerWidth());
+ lambda(inst.args[0], Arg::Use, GP, pointerWidth());
- inst.args[0].special()->forEachArg(inst, scopedLambda<Inst::EachArgCallback>(functor));
+ inst.args[0].special()->forEachArg(inst, lambda);
}
template<typename... Arguments>
Added: trunk/Source/_javascript_Core/b3/air/AirFormTable.h (0 => 214827)
--- trunk/Source/_javascript_Core/b3/air/AirFormTable.h (rev 0)
+++ trunk/Source/_javascript_Core/b3/air/AirFormTable.h 2017-04-03 18:55:34 UTC (rev 214827)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+#include "AirArg.h"
+
+namespace JSC { namespace B3 { namespace Air {
+
+static const uint8_t formRoleShift = 0;
+static const uint8_t formRoleMask = 15;
+static const uint8_t formBankShift = 4;
+static const uint8_t formBankMask = 1;
+static const uint8_t formWidthShift = 5;
+static const uint8_t formWidthMask = 3;
+static const uint8_t formInvalidShift = 7;
+
+#define ENCODE_INST_FORM(role, bank, width) (static_cast<uint8_t>(role) << formRoleShift | static_cast<uint8_t>(bank) << formBankShift | static_cast<uint8_t>(width) << formWidthShift)
+
+#define INVALID_INST_FORM (1 << formInvalidShift)
+
+JS_EXPORT_PRIVATE extern uint8_t g_formTable[];
+
+inline Arg::Role decodeFormRole(uint8_t value)
+{
+ return static_cast<Arg::Role>((value >> formRoleShift) & formRoleMask);
+}
+
+inline Bank decodeFormBank(uint8_t value)
+{
+ return static_cast<Bank>((value >> formBankShift) & formBankMask);
+}
+
+inline Width decodeFormWidth(uint8_t value)
+{
+ return static_cast<Width>((value >> formWidthShift) & formWidthMask);
+}
+
+} } } // namespace JSC::B3::Air
+
+#endif // ENABLE(B3_JIT)
+
Modified: trunk/Source/_javascript_Core/b3/air/AirInst.h (214826 => 214827)
--- trunk/Source/_javascript_Core/b3/air/AirInst.h 2017-04-03 18:44:05 UTC (rev 214826)
+++ trunk/Source/_javascript_Core/b3/air/AirInst.h 2017-04-03 18:55:34 UTC (rev 214827)
@@ -45,7 +45,6 @@
struct GenerationContext;
struct Inst {
-public:
typedef Vector<Arg, 3> ArgList;
Inst()
@@ -207,6 +206,11 @@
ArgList args;
Value* origin; // The B3::Value that this originated from.
Kind kind;
+
+private:
+ template<typename Func>
+ void forEachArgSimple(const Func&);
+ void forEachArgCustom(ScopedLambda<EachArgCallback>);
};
} } } // namespace JSC::B3::Air
Modified: trunk/Source/_javascript_Core/b3/air/opcode_generator.rb (214826 => 214827)
--- trunk/Source/_javascript_Core/b3/air/opcode_generator.rb 2017-04-03 18:44:05 UTC (rev 214826)
+++ trunk/Source/_javascript_Core/b3/air/opcode_generator.rb 2017-04-03 18:55:34 UTC (rev 214827)
@@ -54,7 +54,7 @@
def self.widthCode(width)
if width == "Ptr"
- "pointerWidth()"
+ "POINTER_WIDTH"
else
"Width#{width}"
end
@@ -94,6 +94,10 @@
def roleCode
Arg.roleCode(role)
end
+
+ def to_s
+ "#{role}:#{bank}:#{width}"
+ end
end
class Overload
@@ -514,7 +518,7 @@
| outp |
outp.puts "namespace JSC { namespace B3 { namespace Air {"
outp.puts "enum Opcode : int16_t {"
- $opcodes.keys.sort.each {
+ $opcodes.keys.each {
| opcode |
outp.puts " #{opcode},"
}
@@ -645,10 +649,23 @@
outp.puts "#endif"
end
+maxNumOperands = 0
+$opcodes.values.each {
+ | opcode |
+ next if opcode.custom
+ opcode.overloads.each {
+ | overload |
+ maxNumOperands = overload.signature.length if overload.signature.length > maxNumOperands
+ }
+}
+
+formTableWidth = (maxNumOperands + 1) * maxNumOperands / 2
+
writeH("OpcodeUtils") {
| outp |
outp.puts "#include \"AirCustom.h\""
outp.puts "#include \"AirInst.h\""
+ outp.puts "#include \"AirFormTable.h\""
outp.puts "namespace JSC { namespace B3 { namespace Air {"
outp.puts "inline bool opgenHiddenTruth() { return true; }"
@@ -660,20 +677,35 @@
outp.puts "} while (false)"
outp.puts "template<typename Functor>"
- outp.puts "void Inst::forEachArg(const Functor& functor)"
+ outp.puts "ALWAYS_INLINE void Inst::forEachArg(const Functor& functor)"
outp.puts "{"
- matchInstOverload(outp, :fast, "this") {
- | opcode, overload |
+ outp.puts "switch (kind.opcode) {"
+ $opcodes.values.each {
+ | opcode |
if opcode.custom
- outp.puts "#{opcode.name}Custom::forEachArg(*this, functor);"
- else
- overload.signature.each_with_index {
- | arg, index |
- outp.puts "functor(args[#{index}], Arg::#{arg.roleCode}, #{arg.bank}P, #{arg.widthCode});"
- }
+ outp.puts "case Opcode::#{opcode.name}:"
end
}
+ outp.puts "forEachArgCustom(scopedLambdaRef<EachArgCallback>(functor));"
+ outp.puts "return;"
+ outp.puts "default:"
+ outp.puts "forEachArgSimple(functor);"
+ outp.puts "return;"
outp.puts "}"
+ outp.puts "}"
+
+ outp.puts "template<typename Func>"
+ outp.puts "ALWAYS_INLINE void Inst::forEachArgSimple(const Func& func)"
+ outp.puts "{"
+ outp.puts " size_t numOperands = args.size();"
+ outp.puts " size_t formOffset = (numOperands - 1) * numOperands / 2;"
+ outp.puts " uint8_t* formBase = g_formTable + kind.opcode * #{formTableWidth} + formOffset;"
+ outp.puts " for (size_t i = 0; i < numOperands; ++i) {"
+ outp.puts " uint8_t form = formBase[i];"
+ outp.puts " ASSERT(!(form & (1 << formInvalidShift)));"
+ outp.puts " func(args[i], decodeFormRole(form), decodeFormBank(form), decodeFormWidth(form));"
+ outp.puts " }"
+ outp.puts "}"
outp.puts "template<typename... Arguments>"
outp.puts "ALWAYS_INLINE bool isValidForm(Opcode opcode, Arguments... arguments)"
@@ -789,6 +821,56 @@
outp.puts "}"
outp.puts "} // namespace WTF"
outp.puts "namespace JSC { namespace B3 { namespace Air {"
+
+ outp.puts "uint8_t g_formTable[#{$opcodes.size * formTableWidth}] = {"
+ $opcodes.values.each {
+ | opcode |
+ overloads = [nil] * (maxNumOperands + 1)
+ unless opcode.custom
+ opcode.overloads.each {
+ | overload |
+ overloads[overload.signature.length] = overload
+ }
+ end
+
+ (0..maxNumOperands).each {
+ | numOperands |
+ overload = overloads[numOperands]
+ if overload
+ outp.puts "// #{opcode.name} #{overload.signature.join(', ')}"
+ numOperands.times {
+ | index |
+ arg = overload.signature[index]
+ outp.print "ENCODE_INST_FORM(Arg::#{arg.roleCode}, #{arg.bank}P, #{arg.widthCode}), "
+ }
+ else
+ outp.puts "// Invalid: #{opcode.name} with numOperands = #{numOperands}"
+ numOperands.times {
+ outp.print "INVALID_INST_FORM, "
+ }
+ end
+ outp.puts
+ }
+ }
+ outp.puts "};"
+
+ outp.puts "void Inst::forEachArgCustom(ScopedLambda<EachArgCallback> lambda)"
+ outp.puts "{"
+ outp.puts "switch (kind.opcode) {"
+ $opcodes.values.each {
+ | opcode |
+ if opcode.custom
+ outp.puts "case Opcode::#{opcode.name}:"
+ outp.puts "#{opcode.name}Custom::forEachArg(*this, lambda);"
+ outp.puts "break;"
+ end
+ }
+ outp.puts "default:"
+ outp.puts "dataLog(\"Bad call to forEachArgCustom, not custom opcode: \", kind, \"\\n\");"
+ outp.puts "RELEASE_ASSERT_NOT_REACHED();"
+ outp.puts "}"
+ outp.puts "}"
+
outp.puts "bool Inst::isValidForm()"
outp.puts "{"
matchInstOverloadForm(outp, :safe, "this") {
@@ -1140,89 +1222,3 @@
outp.puts "} } } // namespace JSC::B3::Air"
}
-# This is a hack for JSAir. It's a joke.
-File.open("JSAir_opcode.js", "w") {
- | outp |
- outp.puts "\"use strict\";"
- outp.puts "// Generated by opcode_generator.rb from #{$fileName} -- do not edit!"
-
- $opcodes.values.each {
- | opcode |
- outp.puts "const #{opcode.name} = Symbol(#{opcode.name.inspect});"
- }
-
- outp.puts "function Inst_forEachArg(inst, func)"
- outp.puts "{"
- outp.puts "let replacement;"
- outp.puts "switch (inst.opcode) {"
- $opcodes.values.each {
- | opcode |
- outp.puts "case Opcode::#{opcode.name}:"
- if opcode.custom
- outp.puts "#{opcode.name}Custom.forEachArg(inst, func);"
- else
- needOverloadSwitch = opcode.overloads.size != 1
- outp.puts "switch (inst.args.length) {" if needOverloadSwitch
- opcode.overloads.each {
- | overload |
- outp.puts "case #{overload.signature.length}:" if needOverloadSwitch
- overload.signature.each_with_index {
- | arg, index |
- outp.puts "inst.visitArg(#{index}, func, Arg.#{arg.roleCode}, #{arg.bank}P, #{arg.width});"
- }
- outp.puts "break;"
- }
- if needOverloadSwitch
- outp.puts "default:"
- outp.puts "throw new Error(\"Bad overload\");"
- outp.puts "break;"
- outp.puts "}"
- end
- end
- outp.puts "break;"
- }
- outp.puts "default:"
- outp.puts "throw \"Bad opcode\";"
- outp.puts "}"
- outp.puts "}"
-
- outp.puts "function Inst_hasNonArgEffects(inst)"
- outp.puts "{"
- outp.puts "switch (inst.opcode) {"
- foundTrue = false
- $opcodes.values.each {
- | opcode |
- if opcode.attributes[:terminal] or opcode.attributes[:effects]
- outp.puts "case Opcode::#{opcode.name}:"
- foundTrue = true
- end
- }
- if foundTrue
- outp.puts "return true;"
- end
- $opcodes.values.each {
- | opcode |
- if opcode.custom
- outp.puts "case Opcode::#{opcode.name}:"
- outp.puts "return #{opcode.name}Custom.hasNonArgNonControlEffects(inst);"
- end
- }
- outp.puts "default:"
- outp.puts "return false;"
- outp.puts "}"
- outp.puts "}"
-
- outp.puts "function opcodeCode(opcode)"
- outp.puts "{"
- outp.puts "switch (opcode) {"
- $opcodes.keys.sort.each_with_index {
- | opcode, index |
- outp.puts "case Opcode::#{opcode}:"
- outp.puts "return #{index}"
- }
- outp.puts "default:"
- outp.puts "throw new Error(\"bad opcode\");"
- outp.puts "}"
- outp.puts "}"
-}
-