Assembler-x86-shared.cpp (11942B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 "mozilla/Maybe.h" 8 9 #include <algorithm> 10 11 #include "jit/AutoWritableJitCode.h" 12 #if defined(JS_CODEGEN_X86) 13 # include "jit/x86/MacroAssembler-x86.h" 14 #elif defined(JS_CODEGEN_X64) 15 # include "jit/x64/MacroAssembler-x64.h" 16 #else 17 # error "Wrong architecture. Only x86 and x64 should build this file!" 18 #endif 19 20 #ifdef _MSC_VER 21 # include <intrin.h> // for __cpuid 22 # if defined(_M_X64) && (_MSC_FULL_VER >= 160040219) 23 # include <immintrin.h> // for _xgetbv 24 # endif 25 #endif 26 27 using namespace js; 28 using namespace js::jit; 29 30 void AssemblerX86Shared::copyJumpRelocationTable(uint8_t* dest) { 31 if (jumpRelocations_.length()) { 32 memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); 33 } 34 } 35 36 void AssemblerX86Shared::copyDataRelocationTable(uint8_t* dest) { 37 if (dataRelocations_.length()) { 38 memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length()); 39 } 40 } 41 42 /* static */ 43 void AssemblerX86Shared::TraceDataRelocations(JSTracer* trc, JitCode* code, 44 CompactBufferReader& reader) { 45 mozilla::Maybe<AutoWritableJitCode> awjc; 46 47 while (reader.more()) { 48 size_t offset = reader.readUnsigned(); 49 MOZ_ASSERT(offset >= sizeof(void*) && offset <= code->instructionsSize()); 50 51 uint8_t* src = code->raw() + offset; 52 void* data = X86Encoding::GetPointer(src); 53 54 #ifdef JS_PUNBOX64 55 // Data relocations can be for Values or for raw pointers. If a Value is 56 // zero-tagged, we can trace it as if it were a raw pointer. If a Value 57 // is not zero-tagged, we have to interpret it as a Value to ensure that the 58 // tag bits are masked off to recover the actual pointer. 59 60 uintptr_t word = reinterpret_cast<uintptr_t>(data); 61 if (word >> JSVAL_TAG_SHIFT) { 62 // This relocation is a Value with a non-zero tag. 63 Value value = Value::fromRawBits(word); 64 MOZ_ASSERT_IF(value.isGCThing(), 65 gc::IsCellPointerValid(value.toGCThing())); 66 TraceManuallyBarrieredEdge(trc, &value, "jit-masm-value"); 67 if (word != value.asRawBits()) { 68 if (awjc.isNothing()) { 69 awjc.emplace(code); 70 } 71 X86Encoding::SetPointer(src, value.bitsAsPunboxPointer()); 72 } 73 continue; 74 } 75 #endif 76 77 // This relocation is a raw pointer or a Value with a zero tag. 78 gc::Cell* cell = static_cast<gc::Cell*>(data); 79 MOZ_ASSERT(gc::IsCellPointerValid(cell)); 80 TraceManuallyBarrieredGenericPointerEdge(trc, &cell, "jit-masm-ptr"); 81 if (cell != data) { 82 if (awjc.isNothing()) { 83 awjc.emplace(code); 84 } 85 X86Encoding::SetPointer(src, cell); 86 } 87 } 88 } 89 90 void AssemblerX86Shared::executableCopy(void* buffer) { 91 masm.executableCopy(buffer); 92 } 93 94 void AssemblerX86Shared::processCodeLabels(uint8_t* rawCode) { 95 for (const CodeLabel& label : codeLabels_) { 96 Bind(rawCode, label); 97 } 98 } 99 100 AssemblerX86Shared::Condition AssemblerX86Shared::InvertCondition( 101 Condition cond) { 102 switch (cond) { 103 case Zero: 104 return NonZero; 105 case NonZero: 106 return Zero; 107 case LessThan: 108 return GreaterThanOrEqual; 109 case LessThanOrEqual: 110 return GreaterThan; 111 case GreaterThan: 112 return LessThanOrEqual; 113 case GreaterThanOrEqual: 114 return LessThan; 115 case Above: 116 return BelowOrEqual; 117 case AboveOrEqual: 118 return Below; 119 case Below: 120 return AboveOrEqual; 121 case BelowOrEqual: 122 return Above; 123 case Overflow: 124 return NoOverflow; 125 case NoOverflow: 126 return Overflow; 127 case Signed: 128 return NotSigned; 129 case NotSigned: 130 return Signed; 131 case Parity: 132 return NoParity; 133 case NoParity: 134 return Parity; 135 } 136 MOZ_CRASH("unexpected condition"); 137 } 138 139 AssemblerX86Shared::Condition AssemblerX86Shared::UnsignedCondition( 140 Condition cond) { 141 switch (cond) { 142 case Zero: 143 case NonZero: 144 return cond; 145 case LessThan: 146 case Below: 147 return Below; 148 case LessThanOrEqual: 149 case BelowOrEqual: 150 return BelowOrEqual; 151 case GreaterThan: 152 case Above: 153 return Above; 154 case AboveOrEqual: 155 case GreaterThanOrEqual: 156 return AboveOrEqual; 157 default: 158 MOZ_CRASH("unexpected condition"); 159 } 160 } 161 162 AssemblerX86Shared::Condition AssemblerX86Shared::ConditionWithoutEqual( 163 Condition cond) { 164 switch (cond) { 165 case LessThan: 166 case LessThanOrEqual: 167 return LessThan; 168 case Below: 169 case BelowOrEqual: 170 return Below; 171 case GreaterThan: 172 case GreaterThanOrEqual: 173 return GreaterThan; 174 case Above: 175 case AboveOrEqual: 176 return Above; 177 default: 178 MOZ_CRASH("unexpected condition"); 179 } 180 } 181 182 AssemblerX86Shared::DoubleCondition AssemblerX86Shared::InvertCondition( 183 DoubleCondition cond) { 184 switch (cond) { 185 case DoubleEqual: 186 return DoubleNotEqualOrUnordered; 187 case DoubleEqualOrUnordered: 188 return DoubleNotEqual; 189 case DoubleNotEqualOrUnordered: 190 return DoubleEqual; 191 case DoubleNotEqual: 192 return DoubleEqualOrUnordered; 193 case DoubleLessThan: 194 return DoubleGreaterThanOrEqualOrUnordered; 195 case DoubleLessThanOrUnordered: 196 return DoubleGreaterThanOrEqual; 197 case DoubleLessThanOrEqual: 198 return DoubleGreaterThanOrUnordered; 199 case DoubleLessThanOrEqualOrUnordered: 200 return DoubleGreaterThan; 201 case DoubleGreaterThan: 202 return DoubleLessThanOrEqualOrUnordered; 203 case DoubleGreaterThanOrUnordered: 204 return DoubleLessThanOrEqual; 205 case DoubleGreaterThanOrEqual: 206 return DoubleLessThanOrUnordered; 207 case DoubleGreaterThanOrEqualOrUnordered: 208 return DoubleLessThan; 209 default: 210 MOZ_CRASH("unexpected condition"); 211 } 212 } 213 214 CPUInfo::SSEVersion CPUInfo::maxSSEVersion = UnknownSSE; 215 CPUInfo::SSEVersion CPUInfo::maxEnabledSSEVersion = UnknownSSE; 216 bool CPUInfo::avxPresent = false; 217 #ifdef ENABLE_WASM_AVX 218 bool CPUInfo::avxEnabled = true; 219 #else 220 bool CPUInfo::avxEnabled = false; 221 #endif 222 bool CPUInfo::popcntPresent = false; 223 bool CPUInfo::bmi1Present = false; 224 bool CPUInfo::bmi2Present = false; 225 bool CPUInfo::lzcntPresent = false; 226 bool CPUInfo::avx2Present = false; 227 bool CPUInfo::fmaPresent = false; 228 bool CPUInfo::f16cPresent = false; 229 230 namespace js { 231 namespace jit { 232 bool CPUFlagsHaveBeenComputed() { return CPUInfo::FlagsHaveBeenComputed(); } 233 } // namespace jit 234 } // namespace js 235 236 static uintptr_t ReadXGETBV() { 237 // We use a variety of low-level mechanisms to get at the xgetbv 238 // instruction, including spelling out the xgetbv instruction as bytes, 239 // because older compilers and assemblers may not recognize the instruction 240 // by name. 241 size_t xcr0EAX = 0; 242 #if defined(_XCR_XFEATURE_ENABLED_MASK) 243 xcr0EAX = _xgetbv(_XCR_XFEATURE_ENABLED_MASK); 244 #elif defined(__GNUC__) 245 // xgetbv returns its results in %eax and %edx, and for our purposes here, 246 // we're only interested in the %eax value. 247 asm(".byte 0x0f, 0x01, 0xd0" : "=a"(xcr0EAX) : "c"(0) : "%edx"); 248 #elif defined(_MSC_VER) && defined(_M_IX86) 249 __asm { 250 xor ecx, ecx 251 _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0 252 mov xcr0EAX, eax 253 } 254 #endif 255 return xcr0EAX; 256 } 257 258 static void ReadCPUInfo(int* flagsEax, int* flagsEbx, int* flagsEcx, 259 int* flagsEdx) { 260 #ifdef _MSC_VER 261 int cpuinfo[4]; 262 __cpuid(cpuinfo, *flagsEax); 263 *flagsEax = cpuinfo[0]; 264 *flagsEbx = cpuinfo[1]; 265 *flagsEcx = cpuinfo[2]; 266 *flagsEdx = cpuinfo[3]; 267 #elif defined(__GNUC__) 268 // Some older 32-bits processors don't fill the ecx register with cpuid, so 269 // clobber it before calling cpuid, so that there's no risk of picking 270 // random bits indicating SSE3/SSE4 are present. Also make sure that it's 271 // set to 0 as an input for BMI detection on all platforms. 272 *flagsEcx = 0; 273 # ifdef JS_CODEGEN_X64 274 asm("cpuid;" 275 : "+a"(*flagsEax), "=b"(*flagsEbx), "+c"(*flagsEcx), "=d"(*flagsEdx)); 276 # else 277 // On x86, preserve ebx. The compiler needs it for PIC mode. 278 asm("mov %%ebx, %%edi;" 279 "cpuid;" 280 "xchg %%edi, %%ebx;" 281 : "+a"(*flagsEax), "=D"(*flagsEbx), "+c"(*flagsEcx), "=d"(*flagsEdx)); 282 # endif 283 #else 284 # error "Unsupported compiler" 285 #endif 286 } 287 288 void CPUInfo::ComputeFlags() { 289 MOZ_ASSERT(!FlagsHaveBeenComputed()); 290 291 int flagsEax = 1; 292 int flagsEbx = 0; 293 int flagsEcx = 0; 294 int flagsEdx = 0; 295 ReadCPUInfo(&flagsEax, &flagsEbx, &flagsEcx, &flagsEdx); 296 297 static constexpr int SSEBit = 1 << 25; 298 static constexpr int SSE2Bit = 1 << 26; 299 static constexpr int SSE3Bit = 1 << 0; 300 static constexpr int SSSE3Bit = 1 << 9; 301 static constexpr int SSE41Bit = 1 << 19; 302 static constexpr int SSE42Bit = 1 << 20; 303 304 if (flagsEcx & SSE42Bit) { 305 maxSSEVersion = SSE4_2; 306 } else if (flagsEcx & SSE41Bit) { 307 maxSSEVersion = SSE4_1; 308 } else if (flagsEcx & SSSE3Bit) { 309 maxSSEVersion = SSSE3; 310 } else if (flagsEcx & SSE3Bit) { 311 maxSSEVersion = SSE3; 312 } else if (flagsEdx & SSE2Bit) { 313 maxSSEVersion = SSE2; 314 } else if (flagsEdx & SSEBit) { 315 maxSSEVersion = SSE; 316 } else { 317 maxSSEVersion = NoSSE; 318 } 319 320 if (maxEnabledSSEVersion != UnknownSSE) { 321 maxSSEVersion = std::min(maxSSEVersion, maxEnabledSSEVersion); 322 } 323 324 static constexpr int AVXBit = 1 << 28; 325 static constexpr int XSAVEBit = 1 << 27; 326 bool avxSupported = (flagsEcx & AVXBit); 327 avxPresent = avxSupported && (flagsEcx & XSAVEBit) && avxEnabled; 328 329 // If the hardware supports AVX, check whether the OS supports it too. 330 if (avxPresent) { 331 size_t xcr0EAX = ReadXGETBV(); 332 static constexpr int xcr0SSEBit = 1 << 1; 333 static constexpr int xcr0AVXBit = 1 << 2; 334 avxPresent = (xcr0EAX & xcr0SSEBit) && (xcr0EAX & xcr0AVXBit); 335 } 336 337 // CMOV instruction are supposed to be supported by all CPU which have SSE2 338 // enabled. While this might be true, this is not guaranteed by any 339 // documentation, nor AMD, nor Intel. 340 static constexpr int CMOVBit = 1 << 15; 341 MOZ_RELEASE_ASSERT(flagsEdx & CMOVBit, 342 "CMOVcc instruction is not recognized by this CPU."); 343 344 static constexpr int POPCNTBit = 1 << 23; 345 popcntPresent = (flagsEcx & POPCNTBit); 346 347 // Use the avxEnabled flag to enable/disable FMA. 348 static constexpr int FMABit = 1 << 12; 349 fmaPresent = (flagsEcx & FMABit) && avxEnabled; 350 351 // Support for the F16C instruction set. Requires AVX support. 352 static constexpr int F16CBit = 1 << 29; 353 f16cPresent = avxPresent && (flagsEcx & F16CBit); 354 355 flagsEax = 0x80000001; 356 ReadCPUInfo(&flagsEax, &flagsEbx, &flagsEcx, &flagsEdx); 357 358 static constexpr int LZCNTBit = 1 << 5; 359 lzcntPresent = (flagsEcx & LZCNTBit); 360 361 flagsEax = 0x7; 362 ReadCPUInfo(&flagsEax, &flagsEbx, &flagsEcx, &flagsEdx); 363 364 // BMI1/2 instructions can have a VEX prefix. If the CPU doesn't support AVX, 365 // it may not be able to decode the VEX prefix. So only enable BMI1 when AVX 366 // is also supported. 367 // 368 // NOTE: This doesn't affect real hardware, because if BMI1 is supported by 369 // the CPU, then AVX is also supported. Emulators on the other hand can 370 // disable specific CPU features and emulate a CPU which supports BMI1, but 371 // not AVX. 372 // 373 // Old QEMU versions (before release 7.2) don't support AVX, but appear to 374 // report BMI1 as supported. When using BMI1 instructions with a VEX prefix, 375 // like for example ANDN, QEMU will then abort because it can't decode ANDN. 376 // Therefore we only enable BMI1 when AVX is also reported as supported. 377 static constexpr int BMI1Bit = 1 << 3; 378 static constexpr int BMI2Bit = 1 << 8; 379 static constexpr int AVX2Bit = 1 << 5; 380 bmi1Present = avxSupported && (flagsEbx & BMI1Bit); 381 bmi2Present = bmi1Present && (flagsEbx & BMI2Bit); 382 avx2Present = avxPresent && (flagsEbx & AVX2Bit); 383 384 MOZ_ASSERT(FlagsHaveBeenComputed()); 385 }