1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "gc/Marking.h"
8 #include "jit/Disassembler.h"
9 #include "jit/JitCompartment.h"
10 #if defined(JS_CODEGEN_X86)
11 # include "jit/x86/MacroAssembler-x86.h"
12 #elif defined(JS_CODEGEN_X64)
13 # include "jit/x64/MacroAssembler-x64.h"
14 #else
15 # error "Wrong architecture. Only x86 and x64 should build this file!"
16 #endif
17 
18 #ifdef _MSC_VER
19 # include <intrin.h> // for __cpuid
20 # if defined(_M_X64) && (_MSC_FULL_VER >= 160040219)
21 #  include <immintrin.h> // for _xgetbv
22 # endif
23 #endif
24 
25 using namespace js;
26 using namespace js::jit;
27 
28 void
copyJumpRelocationTable(uint8_t * dest)29 AssemblerX86Shared::copyJumpRelocationTable(uint8_t* dest)
30 {
31     if (jumpRelocations_.length())
32         memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length());
33 }
34 
35 void
copyDataRelocationTable(uint8_t * dest)36 AssemblerX86Shared::copyDataRelocationTable(uint8_t* dest)
37 {
38     if (dataRelocations_.length())
39         memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length());
40 }
41 
42 void
copyPreBarrierTable(uint8_t * dest)43 AssemblerX86Shared::copyPreBarrierTable(uint8_t* dest)
44 {
45     if (preBarriers_.length())
46         memcpy(dest, preBarriers_.buffer(), preBarriers_.length());
47 }
48 
49 static void
TraceDataRelocations(JSTracer * trc,uint8_t * buffer,CompactBufferReader & reader)50 TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader)
51 {
52     while (reader.more()) {
53         size_t offset = reader.readUnsigned();
54         void* ptr = X86Encoding::GetPointer(buffer + offset);
55 
56 #ifdef JS_PUNBOX64
57         // All pointers on x64 will have the top bits cleared. If those bits
58         // are not cleared, this must be a Value.
59         uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
60         if (word >> JSVAL_TAG_SHIFT) {
61             Value v = Value::fromRawBits(word);
62             TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value");
63             if (word != v.asRawBits()) {
64                 // Only update the code if the Value changed, because the code
65                 // is not writable if we're not moving objects.
66                 X86Encoding::SetPointer(buffer + offset, v.bitsAsPunboxPointer());
67             }
68             continue;
69         }
70 #endif
71 
72         // No barrier needed since these are constants.
73         gc::Cell* cellPtr = reinterpret_cast<gc::Cell*>(ptr);
74         TraceManuallyBarrieredGenericPointerEdge(trc, &cellPtr, "jit-masm-ptr");
75         if (cellPtr != ptr)
76             X86Encoding::SetPointer(buffer + offset, cellPtr);
77     }
78 }
79 
80 
81 void
TraceDataRelocations(JSTracer * trc,JitCode * code,CompactBufferReader & reader)82 AssemblerX86Shared::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
83 {
84     ::TraceDataRelocations(trc, code->raw(), reader);
85 }
86 
87 void
trace(JSTracer * trc)88 AssemblerX86Shared::trace(JSTracer* trc)
89 {
90     for (size_t i = 0; i < jumps_.length(); i++) {
91         RelativePatch& rp = jumps_[i];
92         if (rp.kind == Relocation::JITCODE) {
93             JitCode* code = JitCode::FromExecutable((uint8_t*)rp.target);
94             TraceManuallyBarrieredEdge(trc, &code, "masmrel32");
95             MOZ_ASSERT(code == JitCode::FromExecutable((uint8_t*)rp.target));
96         }
97     }
98     if (dataRelocations_.length()) {
99         CompactBufferReader reader(dataRelocations_);
100         ::TraceDataRelocations(trc, masm.data(), reader);
101     }
102 }
103 
104 void
executableCopy(void * buffer)105 AssemblerX86Shared::executableCopy(void* buffer)
106 {
107     masm.executableCopy(buffer);
108 
109     // Crash diagnostics for bug 1124397. Check the code buffer has not been
110     // poisoned with 0xE5 bytes.
111     static const size_t MinPoisoned = 16;
112     const uint8_t* bytes = (const uint8_t*)buffer;
113     size_t len = size();
114 
115     for (size_t i = 0; i < len; i += MinPoisoned) {
116         if (bytes[i] != 0xE5)
117             continue;
118 
119         size_t startOffset = i;
120         while (startOffset > 0 && bytes[startOffset - 1] == 0xE5)
121             startOffset--;
122 
123         size_t endOffset = i;
124         while (endOffset + 1 < len && bytes[endOffset + 1] == 0xE5)
125             endOffset++;
126 
127         if (endOffset - startOffset < MinPoisoned)
128             continue;
129 
130         volatile uintptr_t dump[5];
131         blackbox = dump;
132         blackbox[0] = uintptr_t(0xABCD4321);
133         blackbox[1] = uintptr_t(len);
134         blackbox[2] = uintptr_t(startOffset);
135         blackbox[3] = uintptr_t(endOffset);
136         blackbox[4] = uintptr_t(0xFFFF8888);
137         MOZ_CRASH("Corrupt code buffer");
138     }
139 }
140 
141 void
processCodeLabels(uint8_t * rawCode)142 AssemblerX86Shared::processCodeLabels(uint8_t* rawCode)
143 {
144     for (size_t i = 0; i < codeLabels_.length(); i++) {
145         CodeLabel label = codeLabels_[i];
146         Bind(rawCode, label.patchAt(), rawCode + label.target()->offset());
147     }
148 }
149 
150 AssemblerX86Shared::Condition
InvertCondition(Condition cond)151 AssemblerX86Shared::InvertCondition(Condition cond)
152 {
153     switch (cond) {
154       case Zero:
155         return NonZero;
156       case NonZero:
157         return Zero;
158       case LessThan:
159         return GreaterThanOrEqual;
160       case LessThanOrEqual:
161         return GreaterThan;
162       case GreaterThan:
163         return LessThanOrEqual;
164       case GreaterThanOrEqual:
165         return LessThan;
166       case Above:
167         return BelowOrEqual;
168       case AboveOrEqual:
169         return Below;
170       case Below:
171         return AboveOrEqual;
172       case BelowOrEqual:
173         return Above;
174       default:
175         MOZ_CRASH("unexpected condition");
176     }
177 }
178 
179 AssemblerX86Shared::Condition
UnsignedCondition(Condition cond)180 AssemblerX86Shared::UnsignedCondition(Condition cond)
181 {
182     switch (cond) {
183       case Zero:
184       case NonZero:
185         return cond;
186       case LessThan:
187       case Below:
188         return Below;
189       case LessThanOrEqual:
190       case BelowOrEqual:
191         return BelowOrEqual;
192       case GreaterThan:
193       case Above:
194         return Above;
195       case AboveOrEqual:
196       case GreaterThanOrEqual:
197         return AboveOrEqual;
198       default:
199         MOZ_CRASH("unexpected condition");
200     }
201 }
202 
203 AssemblerX86Shared::Condition
ConditionWithoutEqual(Condition cond)204 AssemblerX86Shared::ConditionWithoutEqual(Condition cond)
205 {
206     switch (cond) {
207       case LessThan:
208       case LessThanOrEqual:
209           return LessThan;
210       case Below:
211       case BelowOrEqual:
212         return Below;
213       case GreaterThan:
214       case GreaterThanOrEqual:
215         return GreaterThan;
216       case Above:
217       case AboveOrEqual:
218         return Above;
219       default:
220         MOZ_CRASH("unexpected condition");
221     }
222 }
223 
224 void
verifyHeapAccessDisassembly(uint32_t begin,uint32_t end,const Disassembler::HeapAccess & heapAccess)225 AssemblerX86Shared::verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
226                                                 const Disassembler::HeapAccess& heapAccess)
227 {
228 #ifdef DEBUG
229     if (masm.oom())
230         return;
231     Disassembler::VerifyHeapAccess(masm.data() + begin, masm.data() + end, heapAccess);
232 #endif
233 }
234 
235 CPUInfo::SSEVersion CPUInfo::maxSSEVersion = UnknownSSE;
236 CPUInfo::SSEVersion CPUInfo::maxEnabledSSEVersion = UnknownSSE;
237 bool CPUInfo::avxPresent = false;
238 bool CPUInfo::avxEnabled = false;
239 bool CPUInfo::popcntPresent = false;
240 bool CPUInfo::needAmdBugWorkaround = false;
241 
242 static uintptr_t
ReadXGETBV()243 ReadXGETBV()
244 {
245     // We use a variety of low-level mechanisms to get at the xgetbv
246     // instruction, including spelling out the xgetbv instruction as bytes,
247     // because older compilers and assemblers may not recognize the instruction
248     // by name.
249     size_t xcr0EAX = 0;
250 #if defined(_XCR_XFEATURE_ENABLED_MASK)
251     xcr0EAX = _xgetbv(_XCR_XFEATURE_ENABLED_MASK);
252 #elif defined(__GNUC__)
253     // xgetbv returns its results in %eax and %edx, and for our purposes here,
254     // we're only interested in the %eax value.
255     asm(".byte 0x0f, 0x01, 0xd0" : "=a"(xcr0EAX) : "c"(0) : "%edx");
256 #elif defined(_MSC_VER) && defined(_M_IX86)
257     __asm {
258         xor ecx, ecx
259         _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0
260         mov xcr0EAX, eax
261     }
262 #endif
263     return xcr0EAX;
264 }
265 
266 void
SetSSEVersion()267 CPUInfo::SetSSEVersion()
268 {
269     int flagsEAX = 0;
270     int flagsECX = 0;
271     int flagsEDX = 0;
272 
273 #ifdef _MSC_VER
274     int cpuinfo[4];
275     __cpuid(cpuinfo, 1);
276     flagsEAX = cpuinfo[0];
277     flagsECX = cpuinfo[2];
278     flagsEDX = cpuinfo[3];
279 #elif defined(__GNUC__)
280 # ifdef JS_CODEGEN_X64
281     asm (
282          "movl $0x1, %%eax;"
283          "cpuid;"
284          : "=a" (flagsEAX), "=c" (flagsECX), "=d" (flagsEDX)
285          :
286          : "%ebx"
287          );
288 # else
289     // On x86, preserve ebx. The compiler needs it for PIC mode.
290     // Some older processors don't fill the ecx register with cpuid, so clobber
291     // it before calling cpuid, so that there's no risk of picking random bits
292     // indicating SSE3/SSE4 are present.
293     asm (
294          "xor %%ecx, %%ecx;"
295          "movl $0x1, %%eax;"
296          "pushl %%ebx;"
297          "cpuid;"
298          "popl %%ebx;"
299          : "=a" (flagsEAX), "=c" (flagsECX), "=d" (flagsEDX)
300          :
301          :
302          );
303 # endif
304 #else
305 # error "Unsupported compiler"
306 #endif
307 
308     static const int SSEBit = 1 << 25;
309     static const int SSE2Bit = 1 << 26;
310     static const int SSE3Bit = 1 << 0;
311     static const int SSSE3Bit = 1 << 9;
312     static const int SSE41Bit = 1 << 19;
313     static const int SSE42Bit = 1 << 20;
314 
315     if (flagsECX & SSE42Bit)      maxSSEVersion = SSE4_2;
316     else if (flagsECX & SSE41Bit) maxSSEVersion = SSE4_1;
317     else if (flagsECX & SSSE3Bit) maxSSEVersion = SSSE3;
318     else if (flagsECX & SSE3Bit)  maxSSEVersion = SSE3;
319     else if (flagsEDX & SSE2Bit)  maxSSEVersion = SSE2;
320     else if (flagsEDX & SSEBit)   maxSSEVersion = SSE;
321     else                          maxSSEVersion = NoSSE;
322 
323     if (maxEnabledSSEVersion != UnknownSSE)
324         maxSSEVersion = Min(maxSSEVersion, maxEnabledSSEVersion);
325 
326     static const int AVXBit = 1 << 28;
327     static const int XSAVEBit = 1 << 27;
328     avxPresent = (flagsECX & AVXBit) && (flagsECX & XSAVEBit) && avxEnabled;
329 
330     // If the hardware supports AVX, check whether the OS supports it too.
331     if (avxPresent) {
332         size_t xcr0EAX = ReadXGETBV();
333         static const int xcr0SSEBit = 1 << 1;
334         static const int xcr0AVXBit = 1 << 2;
335         avxPresent = (xcr0EAX & xcr0SSEBit) && (xcr0EAX & xcr0AVXBit);
336     }
337 
338     static const int POPCNTBit = 1 << 23;
339 
340     popcntPresent = (flagsECX & POPCNTBit);
341 
342     // Check if we need to work around an AMD CPU bug (see bug 1281759).
343     // We check for family 20 models 0-2. Intel doesn't use family 20 at
344     // this point, so this should only match AMD CPUs.
345     unsigned family = ((flagsEAX >> 20) & 0xff) + ((flagsEAX >> 8) & 0xf);
346     unsigned model = (((flagsEAX >> 16) & 0xf) << 4) + ((flagsEAX >> 4) & 0xf);
347     needAmdBugWorkaround = (family == 20 && model <= 2);
348 }
349 
350 volatile uintptr_t* blackbox = nullptr;
351