Bug 1281759 - Try to work around mysterious AMD crashes. r=sunfish, a=sylvestre
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 08 Jul 2016 17:30:30 +0200
changeset 342256 7af8faeaf115123fa3774221d930fa80aed1fd49
parent 342255 b5ba20fc86c8e75e7f21904f65113a68909839aa
child 342257 65ab7976703a2d769d5fd5e8862ebeb11832c197
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish, sylvestre
bugs1281759
milestone49.0
Bug 1281759 - Try to work around mysterious AMD crashes. r=sunfish, a=sylvestre
js/src/jit/SharedIC.cpp
js/src/jit/x86-shared/Assembler-x86-shared.cpp
js/src/jit/x86-shared/Assembler-x86-shared.h
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -4086,48 +4086,65 @@ ICTypeMonitor_PrimitiveSet::Compiler::ge
 
     EmitStubGuardFailure(masm);
 
     masm.bind(&success);
     EmitReturnFromIC(masm);
     return true;
 }
 
+static void
+MaybeWorkAroundAmdBug(MacroAssembler& masm)
+{
+    // Attempt to work around an AMD bug (see bug 1034706 and bug 1281759), by
+    // inserting a 4-byte NOP.
+#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
+    if (CPUInfo::NeedAmdBugWorkaround())
+        masm.nop(4);
+#endif
+}
+
 bool
 ICTypeMonitor_SingleObject::Compiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure;
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+    MaybeWorkAroundAmdBug(masm);
 
     // Guard on the object's identity.
     Register obj = masm.extractObject(R0, ExtractTemp0);
     Address expectedObject(ICStubReg, ICTypeMonitor_SingleObject::offsetOfObject());
     masm.branchPtr(Assembler::NotEqual, expectedObject, obj, &failure);
+    MaybeWorkAroundAmdBug(masm);
 
     EmitReturnFromIC(masm);
+    MaybeWorkAroundAmdBug(masm);
 
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
 ICTypeMonitor_ObjectGroup::Compiler::generateStubCode(MacroAssembler& masm)
 {
     Label failure;
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+    MaybeWorkAroundAmdBug(masm);
 
     // Guard on the object's ObjectGroup.
     Register obj = masm.extractObject(R0, ExtractTemp0);
     masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), R1.scratchReg());
 
     Address expectedGroup(ICStubReg, ICTypeMonitor_ObjectGroup::offsetOfGroup());
     masm.branchPtr(Assembler::NotEqual, expectedGroup, R1.scratchReg(), &failure);
+    MaybeWorkAroundAmdBug(masm);
 
     EmitReturnFromIC(masm);
+    MaybeWorkAroundAmdBug(masm);
 
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
 ICUpdatedStub::addUpdateStubForValue(JSContext* cx, HandleScript outerScript, HandleObject obj,
--- a/js/src/jit/x86-shared/Assembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.cpp
@@ -185,16 +185,17 @@ AssemblerX86Shared::verifyHeapAccessDisa
 #endif
 }
 
 CPUInfo::SSEVersion CPUInfo::maxSSEVersion = UnknownSSE;
 CPUInfo::SSEVersion CPUInfo::maxEnabledSSEVersion = UnknownSSE;
 bool CPUInfo::avxPresent = false;
 bool CPUInfo::avxEnabled = false;
 bool CPUInfo::popcntPresent = false;
+bool CPUInfo::needAmdBugWorkaround = false;
 
 static uintptr_t
 ReadXGETBV()
 {
     // We use a variety of low-level mechanisms to get at the xgetbv
     // instruction, including spelling out the xgetbv instruction as bytes,
     // because older compilers and assemblers may not recognize the instruction
     // by name.
@@ -213,47 +214,49 @@ ReadXGETBV()
     }
 #endif
     return xcr0EAX;
 }
 
 void
 CPUInfo::SetSSEVersion()
 {
+    int flagsEAX = 0;
+    int flagsECX = 0;
     int flagsEDX = 0;
-    int flagsECX = 0;
 
 #ifdef _MSC_VER
     int cpuinfo[4];
     __cpuid(cpuinfo, 1);
+    flagsEAX = cpuinfo[0];
     flagsECX = cpuinfo[2];
     flagsEDX = cpuinfo[3];
 #elif defined(__GNUC__)
 # ifdef JS_CODEGEN_X64
     asm (
          "movl $0x1, %%eax;"
          "cpuid;"
-         : "=c" (flagsECX), "=d" (flagsEDX)
+         : "=a" (flagsEAX), "=c" (flagsECX), "=d" (flagsEDX)
          :
-         : "%eax", "%ebx"
+         : "%ebx"
          );
 # else
     // On x86, preserve ebx. The compiler needs it for PIC mode.
     // Some older processors don't fill the ecx register with cpuid, so clobber
     // it before calling cpuid, so that there's no risk of picking random bits
     // indicating SSE3/SSE4 are present.
     asm (
          "xor %%ecx, %%ecx;"
          "movl $0x1, %%eax;"
          "pushl %%ebx;"
          "cpuid;"
          "popl %%ebx;"
-         : "=c" (flagsECX), "=d" (flagsEDX)
+         : "=a" (flagsEAX), "=c" (flagsECX), "=d" (flagsEDX)
          :
-         : "%eax"
+         :
          );
 # endif
 #else
 # error "Unsupported compiler"
 #endif
 
     static const int SSEBit = 1 << 25;
     static const int SSE2Bit = 1 << 26;
@@ -283,11 +286,18 @@ CPUInfo::SetSSEVersion()
         static const int xcr0SSEBit = 1 << 1;
         static const int xcr0AVXBit = 1 << 2;
         avxPresent = (xcr0EAX & xcr0SSEBit) && (xcr0EAX & xcr0AVXBit);
     }
 
     static const int POPCNTBit = 1 << 23;
 
     popcntPresent = (flagsECX & POPCNTBit);
+
+    // Check if we need to work around an AMD CPU bug (see bug 1281759).
+    // We check for family 20 models 0-2. Intel doesn't use family 20 at
+    // this point, so this should only match AMD CPUs.
+    unsigned family = ((flagsEAX >> 20) & 0xff) + ((flagsEAX >> 8) & 0xf);
+    unsigned model = (((flagsEAX >> 16) & 0xf) << 4) + ((flagsEAX >> 4) & 0xf);
+    needAmdBugWorkaround = (family == 20 && model <= 2);
 }
 
 volatile uintptr_t* blackbox = nullptr;
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -219,32 +219,34 @@ class CPUInfo
     }
 
   private:
     static SSEVersion maxSSEVersion;
     static SSEVersion maxEnabledSSEVersion;
     static bool avxPresent;
     static bool avxEnabled;
     static bool popcntPresent;
+    static bool needAmdBugWorkaround;
 
     static void SetSSEVersion();
 
   public:
     static bool IsSSE2Present() {
 #ifdef JS_CODEGEN_X64
         return true;
 #else
         return GetSSEVersion() >= SSE2;
 #endif
     }
     static bool IsSSE3Present()  { return GetSSEVersion() >= SSE3; }
     static bool IsSSSE3Present() { return GetSSEVersion() >= SSSE3; }
     static bool IsSSE41Present() { return GetSSEVersion() >= SSE4_1; }
     static bool IsSSE42Present() { return GetSSEVersion() >= SSE4_2; }
     static bool IsPOPCNTPresent() { return popcntPresent; }
+    static bool NeedAmdBugWorkaround() { return needAmdBugWorkaround; }
 
 #ifdef JS_CODEGEN_X86
     static void SetFloatingPointDisabled() { maxEnabledSSEVersion = NoSSE; avxEnabled = false; }
 #endif
     static void SetSSE3Disabled() { maxEnabledSSEVersion = SSE2; avxEnabled = false; }
     static void SetSSE4Disabled() { maxEnabledSSEVersion = SSSE3; avxEnabled = false; }
     static void SetAVXEnabled() { avxEnabled = true; }
 };
@@ -906,16 +908,17 @@ class AssemblerX86Shared : public Assemb
             // Thread the jump list through the unpatched jump targets.
             label->use(j.offset());
         }
         return j;
     }
 
   public:
     void nop() { masm.nop(); }
+    void nop(size_t n) { masm.insert_nop(n); }
     void j(Condition cond, Label* label) { jSrc(cond, label); }
     void jmp(Label* label) { jmpSrc(label); }
     void j(Condition cond, RepatchLabel* label) { jSrc(cond, label); }
     void jmp(RepatchLabel* label) { jmpSrc(label); }
 
     void j(Condition cond, wasm::JumpTarget target) {
         Label l;
         j(cond, &l);