Bug 1268024: Add an unaligned access trap; r=luke
authorBenjamin Bouvier <benj@benj.me>
Thu, 30 Jun 2016 14:42:09 +0200
changeset 303781 7028215a4aafa0d12c97aee9507d51cbd2e044f8
parent 303780 d21d1aa4234236858f9c8c0f7fa280941310146a
child 303782 f1c67b926f98ef9bef59aedd43c71e995b8b8ef3
push id79180
push userbbouvier@mozilla.com
push dateWed, 06 Jul 2016 13:35:19 +0000
treeherdermozilla-inbound@f1c67b926f98 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1268024
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1268024: Add an unaligned access trap; r=luke MozReview-Commit-ID: HvAF3lvlfw3
js/src/asmjs/WasmCode.cpp
js/src/asmjs/WasmCode.h
js/src/asmjs/WasmGenerator.cpp
js/src/asmjs/WasmJS.cpp
js/src/asmjs/WasmModule.h
js/src/asmjs/WasmStubs.cpp
js/src/asmjs/WasmTypes.cpp
js/src/asmjs/WasmTypes.h
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/arm/Architecture-arm.cpp
js/src/jit/arm/Architecture-arm.h
js/src/jit/arm/Assembler-arm.h
js/src/jit/arm64/Assembler-arm64.h
js/src/jit/x86-shared/Assembler-x86-shared.h
js/src/js.msg
js/src/jscntxt.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -208,16 +208,17 @@ CodeSegment::create(JSContext* cx,
     if (!cs->bytes_)
         return nullptr;
 
     cs->functionCodeLength_ = linkData.functionCodeLength;
     cs->codeLength_ = bytecode.length();
     cs->globalDataLength_ = linkData.globalDataLength;
     cs->interruptCode_ = cs->code() + linkData.interruptOffset;
     cs->outOfBoundsCode_ = cs->code() + linkData.outOfBoundsOffset;
+    cs->unalignedAccessCode_ = cs->code() + linkData.unalignedAccessOffset;
 
     {
         JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
         AutoFlushICache afc("CodeSegment::create");
         AutoFlushICache::setRange(uintptr_t(cs->code()), cs->codeLength());
 
         memcpy(cs->code(), bytecode.begin(), bytecode.length());
         StaticallyLink(*cs, linkData, cx);
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -42,20 +42,21 @@ class CodeSegment
     // the range [codeLength, codeLength + globalDataLength). The range
     // [0, functionCodeLength) is the subrange of [0, codeLength) which contains
     // function code.
     uint8_t* bytes_;
     uint32_t functionCodeLength_;
     uint32_t codeLength_;
     uint32_t globalDataLength_;
 
-    // These are pointers into code for two stubs used for asynchronous
+    // These are pointers into code for stubs used for asynchronous
     // signal-handler control-flow transfer.
     uint8_t* interruptCode_;
     uint8_t* outOfBoundsCode_;
+    uint8_t* unalignedAccessCode_;
 
     // The profiling mode may be changed dynamically.
     bool profilingEnabled_;
 
     CodeSegment() { PodZero(this); }
     template <class> friend struct js::MallocProvider;
 
     CodeSegment(const CodeSegment&) = delete;
@@ -75,16 +76,17 @@ class CodeSegment
     uint8_t* code() const { return bytes_; }
     uint8_t* globalData() const { return bytes_ + codeLength_; }
     uint32_t codeLength() const { return codeLength_; }
     uint32_t globalDataLength() const { return globalDataLength_; }
     uint32_t totalLength() const { return codeLength_ + globalDataLength_; }
 
     uint8_t* interruptCode() const { return interruptCode_; }
     uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
+    uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
 
     // The range [0, functionBytes) is a subrange of [0, codeBytes) that
     // contains only function body code, not the stub code. This distinction is
     // used by the async interrupt handler to only interrupt when the pc is in
     // function code which, in turn, simplifies reasoning about how stubs
     // enter/exit.
 
     bool containsFunctionPC(void* pc) const {
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -414,16 +414,17 @@ ModuleGenerator::finishCodegen()
 
     interruptExit.offsetBy(offsetInWhole);
     if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit))
         return false;
 
     // Fill in LinkData with the offsets of these stubs.
 
     linkData_.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin;
+    linkData_.unalignedAccessOffset = jumpTargets[JumpTarget::UnalignedAccess].begin;
     linkData_.interruptOffset = interruptExit.begin;
 
     // Only call convertOutOfRangeBranchesToThunks after all other codegen that may
     // emit new jumps to JumpTargets has finished.
 
     if (!convertOutOfRangeBranchesToThunks())
         return false;
 
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -30,16 +30,19 @@ using namespace js;
 using namespace js::wasm;
 
 bool
 wasm::HasCompilerSupport(ExclusiveContext* cx)
 {
     if (!cx->jitSupportsFloatingPoint())
         return false;
 
+    if (!cx->jitSupportsUnalignedAccesses())
+        return false;
+
 #if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
     return false;
 #else
     return true;
 #endif
 }
 
 static bool
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -36,16 +36,17 @@ namespace wasm {
 // in Module.
 
 struct LinkDataCacheablePod
 {
     uint32_t functionCodeLength;
     uint32_t globalDataLength;
     uint32_t interruptOffset;
     uint32_t outOfBoundsOffset;
+    uint32_t unalignedAccessOffset;
 
     LinkDataCacheablePod() { mozilla::PodZero(this); }
 };
 
 struct LinkData : LinkDataCacheablePod
 {
     LinkDataCacheablePod& pod() { return *this; }
     const LinkDataCacheablePod& pod() const { return *this; }
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -929,16 +929,17 @@ wasm::GenerateJumpTarget(MacroAssembler&
 {
     switch (target) {
       case JumpTarget::StackOverflow:
         return GenerateStackOverflow(masm);
       case JumpTarget::Throw:
         return GenerateThrow(masm);
       case JumpTarget::BadIndirectCall:
       case JumpTarget::OutOfBounds:
+      case JumpTarget::UnalignedAccess:
       case JumpTarget::Unreachable:
       case JumpTarget::IntegerOverflow:
       case JumpTarget::InvalidConversionToInteger:
       case JumpTarget::IntegerDivideByZero:
       case JumpTarget::ImpreciseSimdConversion:
         return GenerateTrapStub(masm, Trap(target));
       case JumpTarget::Limit:
         break;
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -119,16 +119,19 @@ HandleTrap(int32_t trapIndex)
         errorNumber = JSMSG_WASM_BAD_IND_CALL;
         break;
       case Trap::ImpreciseSimdConversion:
         errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
         break;
       case Trap::OutOfBounds:
         errorNumber = JSMSG_BAD_INDEX;
         break;
+      case Trap::UnalignedAccess:
+        errorNumber = JSMSG_WASM_UNALIGNED_ACCESS;
+        break;
       default:
         MOZ_CRASH("unexpected trap");
     }
 
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber);
 }
 
 static int32_t
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -727,16 +727,18 @@ enum class Trap
     // An integer arithmetic operation led to an overflow.
     IntegerOverflow,
     // Trying to coerce NaN to an integer.
     InvalidConversionToInteger,
     // Integer division by zero.
     IntegerDivideByZero,
     // Out of bounds on wasm memory accesses and asm.js SIMD/atomic accesses.
     OutOfBounds,
+    // Unaligned memory access.
+    UnalignedAccess,
     // Bad signature for an indirect call.
     BadIndirectCall,
 
     // (asm.js only) SIMD float to int conversion failed because the input
     // wasn't in bounds.
     ImpreciseSimdConversion,
 
     Limit
@@ -750,16 +752,17 @@ enum class Trap
 enum class JumpTarget
 {
     // Traps
     Unreachable = unsigned(Trap::Unreachable),
     IntegerOverflow = unsigned(Trap::IntegerOverflow),
     InvalidConversionToInteger = unsigned(Trap::InvalidConversionToInteger),
     IntegerDivideByZero = unsigned(Trap::IntegerDivideByZero),
     OutOfBounds = unsigned(Trap::OutOfBounds),
+    UnalignedAccess = unsigned(Trap::UnalignedAccess),
     BadIndirectCall = unsigned(Trap::BadIndirectCall),
     ImpreciseSimdConversion = unsigned(Trap::ImpreciseSimdConversion),
     // Non-traps
     StackOverflow,
     Throw,
     Limit
 };
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -3473,16 +3473,22 @@ jit::TraceJitScripts(JSTracer* trc, JSSc
 
 bool
 jit::JitSupportsFloatingPoint()
 {
     return js::jit::MacroAssembler::SupportsFloatingPoint();
 }
 
 bool
+jit::JitSupportsUnalignedAccesses()
+{
+    return js::jit::MacroAssembler::SupportsUnalignedAccesses();
+}
+
+bool
 jit::JitSupportsSimd()
 {
     return js::jit::MacroAssembler::SupportsSimd();
 }
 
 bool
 jit::JitSupportsAtomics()
 {
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -198,15 +198,16 @@ bool OffThreadCompilationAvailable(JSCon
 void ForbidCompilation(JSContext* cx, JSScript* script);
 
 void PurgeCaches(JSScript* script);
 size_t SizeOfIonData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf);
 void DestroyJitScripts(FreeOp* fop, JSScript* script);
 void TraceJitScripts(JSTracer* trc, JSScript* script);
 
 bool JitSupportsFloatingPoint();
+bool JitSupportsUnalignedAccesses();
 bool JitSupportsSimd();
 bool JitSupportsAtomics();
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Ion_h */
--- a/js/src/jit/arm/Architecture-arm.cpp
+++ b/js/src/jit/arm/Architecture-arm.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/arm/Architecture-arm.h"
 
-#if !defined(JS_ARM_SIMULATOR) && !defined(__APPLE__)
+#if !defined(JS_SIMULATOR_ARM) && !defined(__APPLE__)
 #include <elf.h>
 #endif
 
 #include <fcntl.h>
 #include <unistd.h>
 
 #include "jit/arm/Assembler-arm.h"
 #include "jit/RegisterSets.h"
@@ -36,17 +36,16 @@
 # if !defined(HWCAP_VFPD32)
 #  define HWCAP_VFPD32    (1 << 19) /* set if VFP has 32 regs (not 16) */
 # endif
 #endif
 
 namespace js {
 namespace jit {
 
-
 // Parse the Linux kernel cpuinfo features. This is also used to parse the
 // override features which has some extensions: 'armv7', 'align' and 'hardfp'.
 static uint32_t
 ParseARMCpuFeatures(const char* features, bool override = false)
 {
     uint32_t flags = 0;
 
     for (;;) {
@@ -264,16 +263,22 @@ InitARMFlags()
 
 uint32_t
 GetARMFlags()
 {
     MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
     return armHwCapFlags;
 }
 
+bool HasARMv7()
+{
+    MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
+    return armHwCapFlags & HWCAP_ARMv7;
+}
+
 bool HasMOVWT()
 {
     MOZ_ASSERT(armHwCapFlags != HWCAP_UNINITIALIZED);
     return armHwCapFlags & HWCAP_ARMv7;
 }
 
 bool HasLDSTREXBHD()
 {
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -571,16 +571,17 @@ class VFPRegister
     }
 
 };
 
 // The only floating point register set that we work with are the VFP Registers.
 typedef VFPRegister FloatRegister;
 
 uint32_t GetARMFlags();
+bool HasARMv7();
 bool HasMOVWT();
 bool HasLDSTREXBHD();           // {LD,ST}REX{B,H,D}
 bool HasDMBDSBISB();            // DMB, DSB, and ISB
 bool HasVFPv3();
 bool HasVFP();
 bool Has32DP();
 bool HasIDIV();
 
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1747,16 +1747,19 @@ class Assembler : public AssemblerShared
 
   public:
     static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
     static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
     static bool SupportsFloatingPoint() {
         return HasVFP();
     }
+    static bool SupportsUnalignedAccesses() {
+        return HasARMv7();
+    }
     static bool SupportsSimd() {
         return js::jit::SupportsSimd;
     }
 
   protected:
     void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) {
         enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind));
         if (kind == Relocation::JITCODE)
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -282,16 +282,17 @@ class Assembler : public vixl::Assembler
     }
     static uint8_t* PatchableJumpAddress(JitCode* code, uint32_t index) {
         return code->raw() + index;
     }
     void setPrinter(Sprinter* sp) {
     }
 
     static bool SupportsFloatingPoint() { return true; }
+    static bool SupportsUnalignedAccesses() { return true; }
     static bool SupportsSimd() { return js::jit::SupportsSimd; }
 
     // Tracks a jump that is patchable after finalization.
     void addJumpRelocation(BufferOffset src, Relocation::Kind reloc);
 
   protected:
     // Add a jump whose target is unknown until finalization.
     // The jump may not be patched at runtime.
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -1096,16 +1096,17 @@ class AssemblerX86Shared : public Assemb
     }
 
     static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
     static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
     static bool HasSSSE3() { return CPUInfo::IsSSSE3Present(); }
     static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
     static bool HasPOPCNT() { return CPUInfo::IsPOPCNTPresent(); }
     static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); }
+    static bool SupportsUnalignedAccesses() { return true; }
     static bool SupportsSimd() { return CPUInfo::IsSSE2Present(); }
     static bool HasAVX() { return CPUInfo::IsAVXPresent(); }
 
     void cmpl(Register rhs, Register lhs) {
         masm.cmpl_rr(rhs.encoding(), lhs.encoding());
     }
     void cmpl(const Operand& rhs, Register lhs) {
         switch (rhs.kind()) {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -347,16 +347,17 @@ MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1
 MSG_DEF(JSMSG_WASM_BAD_IND_CALL,       0, JSEXN_ERR,         "wasm indirect call signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be an ArrayBuffer or typed array object")
 MSG_DEF(JSMSG_WASM_BAD_MOD_ARG,        0, JSEXN_TYPEERR,     "first argument must be a WebAssembly.Module")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR,     "second argument, if present, must be an object")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_ERR,         "unreachable executed")
 MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW,   0, JSEXN_ERR,         "integer overflow")
 MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_ERR,         "invalid conversion to integer")
 MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_ERR,         "integer divide by zero")
+MSG_DEF(JSMSG_WASM_UNALIGNED_ACCESS,   0, JSEXN_ERR,         "unaligned memory access")
 MSG_DEF(JSMSG_WASM_OVERRECURSED,       0, JSEXN_INTERNALERR, "call stack exhausted")
 
 // Proxy
 MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE,   2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
 MSG_DEF(JSMSG_BAD_GETPROTOTYPEOF_TRAP_RETURN,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler returned a non-object, non-null value")
 MSG_DEF(JSMSG_INCONSISTENT_GETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy getPrototypeOf handler didn't return the target object's prototype")
 MSG_DEF(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy setPrototypeOf handler returned false")
 MSG_DEF(JSMSG_PROXY_ISEXTENSIBLE_RETURNED_FALSE,0,JSEXN_TYPEERR,"proxy isExtensible handler must return the same extensibility as target")
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -205,16 +205,17 @@ class ExclusiveContext : public ContextF
     void* runtimeAddressForJit() { return runtime_; }
     void* runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
     void* stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
     void* stackLimitAddressForJitCode(StackKind kind);
     uintptr_t stackLimit(StackKind kind) { return runtime_->mainThread.nativeStackLimit[kind]; }
     size_t gcSystemPageSize() { return gc::SystemPageSize(); }
     bool canUseSignalHandlers() const { return runtime_->canUseSignalHandlers(); }
     bool jitSupportsFloatingPoint() const { return runtime_->jitSupportsFloatingPoint; }
+    bool jitSupportsUnalignedAccesses() const { return runtime_->jitSupportsUnalignedAccesses; }
     bool jitSupportsSimd() const { return runtime_->jitSupportsSimd; }
     bool lcovEnabled() const { return runtime_->lcovOutput.isEnabled(); }
 
     // Thread local data that may be accessed freely.
     DtoaState* dtoaState() {
         return perThreadData->dtoaState;
     }
 
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -233,16 +233,17 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     atomsCompartment_(nullptr),
     staticStrings(nullptr),
     commonNames(nullptr),
     permanentAtoms(nullptr),
     wellKnownSymbols(nullptr),
     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
     preserveWrapperCallback(nullptr),
     jitSupportsFloatingPoint(false),
+    jitSupportsUnalignedAccesses(false),
     jitSupportsSimd(false),
     ionPcScriptCache(nullptr),
     scriptEnvironmentPreparer(nullptr),
     ctypesActivityCallback(nullptr),
     windowProxyClass_(nullptr),
     offthreadIonCompilationEnabled_(true),
     parallelParsingEnabled_(true),
     autoWritableJitCodeActive_(false),
@@ -345,16 +346,17 @@ JSRuntime::init(uint32_t maxbytes, uint3
 
 #ifdef JS_SIMULATOR
     simulator_ = js::jit::Simulator::Create();
     if (!simulator_)
         return false;
 #endif
 
     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
+    jitSupportsUnalignedAccesses = js::jit::JitSupportsUnalignedAccesses();
     jitSupportsSimd = js::jit::JitSupportsSimd();
 
     signalHandlersInstalled_ = wasm::EnsureSignalHandlersInstalled(this);
     canUseSignalHandlers_ = signalHandlersInstalled_ && !SignalBasedTriggersDisabled();
 
     if (!spsProfiler.init())
         return false;
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1181,18 +1181,19 @@ struct JSRuntime : public JS::shadow::Ru
   private:
     js::ScriptDataTable scriptDataTable_;
   public:
     js::ScriptDataTable& scriptDataTable(js::AutoLockForExclusiveAccess& lock) {
         MOZ_ASSERT(currentThreadHasExclusiveAccess());
         return scriptDataTable_;
     }
 
-    bool                jitSupportsFloatingPoint;
-    bool                jitSupportsSimd;
+    bool jitSupportsFloatingPoint;
+    bool jitSupportsUnalignedAccesses;
+    bool jitSupportsSimd;
 
     // Cache for jit::GetPcScript().
     js::jit::PcScriptCache* ionPcScriptCache;
 
     js::ScriptEnvironmentPreparer* scriptEnvironmentPreparer;
 
     js::CTypesActivityCallback  ctypesActivityCallback;