1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jit/x64/MacroAssembler-x64.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,396 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jit/x64/MacroAssembler-x64.h" 1.11 + 1.12 +#include "jit/Bailouts.h" 1.13 +#include "jit/BaselineFrame.h" 1.14 +#include "jit/IonFrames.h" 1.15 +#include "jit/JitCompartment.h" 1.16 +#include "jit/MoveEmitter.h" 1.17 + 1.18 +using namespace js; 1.19 +using namespace js::jit; 1.20 + 1.21 +void 1.22 +MacroAssemblerX64::loadConstantDouble(double d, const FloatRegister &dest) 1.23 +{ 1.24 + if (maybeInlineDouble(d, dest)) 1.25 + return; 1.26 + 1.27 + if (!doubleMap_.initialized()) { 1.28 + enoughMemory_ &= doubleMap_.init(); 1.29 + if (!enoughMemory_) 1.30 + return; 1.31 + } 1.32 + size_t doubleIndex; 1.33 + if (DoubleMap::AddPtr p = doubleMap_.lookupForAdd(d)) { 1.34 + doubleIndex = p->value(); 1.35 + } else { 1.36 + doubleIndex = doubles_.length(); 1.37 + enoughMemory_ &= doubles_.append(Double(d)); 1.38 + enoughMemory_ &= doubleMap_.add(p, d, doubleIndex); 1.39 + if (!enoughMemory_) 1.40 + return; 1.41 + } 1.42 + Double &dbl = doubles_[doubleIndex]; 1.43 + JS_ASSERT(!dbl.uses.bound()); 1.44 + 1.45 + // The constants will be stored in a pool appended to the text (see 1.46 + // finish()), so they will always be a fixed distance from the 1.47 + // instructions which reference them. This allows the instructions to use 1.48 + // PC-relative addressing. Use "jump" label support code, because we need 1.49 + // the same PC-relative address patching that jumps use. 1.50 + JmpSrc j = masm.movsd_ripr(dest.code()); 1.51 + JmpSrc prev = JmpSrc(dbl.uses.use(j.offset())); 1.52 + masm.setNextJump(j, prev); 1.53 +} 1.54 + 1.55 +void 1.56 +MacroAssemblerX64::loadConstantFloat32(float f, const FloatRegister &dest) 1.57 +{ 1.58 + if (maybeInlineFloat(f, dest)) 1.59 + return; 1.60 + 1.61 + if (!floatMap_.initialized()) { 1.62 + enoughMemory_ &= floatMap_.init(); 1.63 + if (!enoughMemory_) 1.64 + return; 1.65 + } 1.66 + size_t floatIndex; 1.67 + if (FloatMap::AddPtr p = floatMap_.lookupForAdd(f)) { 1.68 + floatIndex = p->value(); 1.69 + } else { 1.70 + floatIndex = floats_.length(); 1.71 + enoughMemory_ &= floats_.append(Float(f)); 1.72 + enoughMemory_ &= floatMap_.add(p, f, floatIndex); 1.73 + if (!enoughMemory_) 1.74 + return; 1.75 + } 1.76 + Float &flt = floats_[floatIndex]; 1.77 + JS_ASSERT(!flt.uses.bound()); 1.78 + 1.79 + // See comment in loadConstantDouble 1.80 + JmpSrc j = masm.movss_ripr(dest.code()); 1.81 + JmpSrc prev = JmpSrc(flt.uses.use(j.offset())); 1.82 + masm.setNextJump(j, prev); 1.83 +} 1.84 + 1.85 +void 1.86 +MacroAssemblerX64::finish() 1.87 +{ 1.88 + if (!doubles_.empty()) 1.89 + masm.align(sizeof(double)); 1.90 + for (size_t i = 0; i < doubles_.length(); i++) { 1.91 + Double &dbl = doubles_[i]; 1.92 + bind(&dbl.uses); 1.93 + masm.doubleConstant(dbl.value); 1.94 + } 1.95 + 1.96 + if (!floats_.empty()) 1.97 + masm.align(sizeof(float)); 1.98 + for (size_t i = 0; i < floats_.length(); i++) { 1.99 + Float &flt = floats_[i]; 1.100 + bind(&flt.uses); 1.101 + masm.floatConstant(flt.value); 1.102 + } 1.103 + 1.104 + MacroAssemblerX86Shared::finish(); 1.105 +} 1.106 + 1.107 +void 1.108 +MacroAssemblerX64::setupABICall(uint32_t args) 1.109 +{ 1.110 + JS_ASSERT(!inCall_); 1.111 + inCall_ = true; 1.112 + 1.113 + args_ = args; 1.114 + passedIntArgs_ = 0; 1.115 + passedFloatArgs_ = 0; 1.116 + stackForCall_ = ShadowStackSpace; 1.117 +} 1.118 + 1.119 +void 1.120 +MacroAssemblerX64::setupAlignedABICall(uint32_t args) 1.121 +{ 1.122 + setupABICall(args); 1.123 + dynamicAlignment_ = false; 1.124 +} 1.125 + 1.126 +void 1.127 +MacroAssemblerX64::setupUnalignedABICall(uint32_t args, const Register &scratch) 1.128 +{ 1.129 + setupABICall(args); 1.130 + dynamicAlignment_ = true; 1.131 + 1.132 + movq(rsp, scratch); 1.133 + andq(Imm32(~(StackAlignment - 1)), rsp); 1.134 + push(scratch); 1.135 +} 1.136 + 1.137 +void 1.138 +MacroAssemblerX64::passABIArg(const MoveOperand &from, MoveOp::Type type) 1.139 +{ 1.140 + MoveOperand to; 1.141 + switch (type) { 1.142 + case MoveOp::FLOAT32: 1.143 + case MoveOp::DOUBLE: { 1.144 + FloatRegister dest; 1.145 + if (GetFloatArgReg(passedIntArgs_, passedFloatArgs_++, &dest)) { 1.146 + if (from.isFloatReg() && from.floatReg() == dest) { 1.147 + // Nothing to do; the value is in the right register already 1.148 + return; 1.149 + } 1.150 + to = MoveOperand(dest); 1.151 + } else { 1.152 + to = MoveOperand(StackPointer, stackForCall_); 1.153 + switch (type) { 1.154 + case MoveOp::FLOAT32: stackForCall_ += sizeof(float); break; 1.155 + case MoveOp::DOUBLE: stackForCall_ += sizeof(double); break; 1.156 + default: MOZ_ASSUME_UNREACHABLE("Unexpected float register class argument type"); 1.157 + } 1.158 + } 1.159 + break; 1.160 + } 1.161 + case MoveOp::GENERAL: { 1.162 + Register dest; 1.163 + if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) { 1.164 + if (from.isGeneralReg() && from.reg() == dest) { 1.165 + // Nothing to do; the value is in the right register already 1.166 + return; 1.167 + } 1.168 + to = MoveOperand(dest); 1.169 + } else { 1.170 + to = MoveOperand(StackPointer, stackForCall_); 1.171 + stackForCall_ += sizeof(int64_t); 1.172 + } 1.173 + break; 1.174 + } 1.175 + default: 1.176 + MOZ_ASSUME_UNREACHABLE("Unexpected argument type"); 1.177 + } 1.178 + 1.179 + enoughMemory_ = moveResolver_.addMove(from, to, type); 1.180 +} 1.181 + 1.182 +void 1.183 +MacroAssemblerX64::passABIArg(const Register ®) 1.184 +{ 1.185 + passABIArg(MoveOperand(reg), MoveOp::GENERAL); 1.186 +} 1.187 + 1.188 +void 1.189 +MacroAssemblerX64::passABIArg(const FloatRegister ®, MoveOp::Type type) 1.190 +{ 1.191 + passABIArg(MoveOperand(reg), type); 1.192 +} 1.193 + 1.194 +void 1.195 +MacroAssemblerX64::callWithABIPre(uint32_t *stackAdjust) 1.196 +{ 1.197 + JS_ASSERT(inCall_); 1.198 + JS_ASSERT(args_ == passedIntArgs_ + passedFloatArgs_); 1.199 + 1.200 + if (dynamicAlignment_) { 1.201 + *stackAdjust = stackForCall_ 1.202 + + ComputeByteAlignment(stackForCall_ + sizeof(intptr_t), 1.203 + StackAlignment); 1.204 + } else { 1.205 + *stackAdjust = stackForCall_ 1.206 + + ComputeByteAlignment(stackForCall_ + framePushed_, 1.207 + StackAlignment); 1.208 + } 1.209 + 1.210 + reserveStack(*stackAdjust); 1.211 + 1.212 + // Position all arguments. 1.213 + { 1.214 + enoughMemory_ &= moveResolver_.resolve(); 1.215 + if (!enoughMemory_) 1.216 + return; 1.217 + 1.218 + MoveEmitter emitter(*this); 1.219 + emitter.emit(moveResolver_); 1.220 + emitter.finish(); 1.221 + } 1.222 + 1.223 +#ifdef DEBUG 1.224 + { 1.225 + Label good; 1.226 + testq(rsp, Imm32(StackAlignment - 1)); 1.227 + j(Equal, &good); 1.228 + breakpoint(); 1.229 + bind(&good); 1.230 + } 1.231 +#endif 1.232 +} 1.233 + 1.234 +void 1.235 +MacroAssemblerX64::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) 1.236 +{ 1.237 + freeStack(stackAdjust); 1.238 + if (dynamicAlignment_) 1.239 + pop(rsp); 1.240 + 1.241 + JS_ASSERT(inCall_); 1.242 + inCall_ = false; 1.243 +} 1.244 + 1.245 +void 1.246 +MacroAssemblerX64::callWithABI(void *fun, MoveOp::Type result) 1.247 +{ 1.248 + uint32_t stackAdjust; 1.249 + callWithABIPre(&stackAdjust); 1.250 + call(ImmPtr(fun)); 1.251 + callWithABIPost(stackAdjust, result); 1.252 +} 1.253 + 1.254 +void 1.255 +MacroAssemblerX64::callWithABI(AsmJSImmPtr imm, MoveOp::Type result) 1.256 +{ 1.257 + uint32_t stackAdjust; 1.258 + callWithABIPre(&stackAdjust); 1.259 + call(imm); 1.260 + callWithABIPost(stackAdjust, result); 1.261 +} 1.262 + 1.263 +static bool 1.264 +IsIntArgReg(Register reg) 1.265 +{ 1.266 + for (uint32_t i = 0; i < NumIntArgRegs; i++) { 1.267 + if (IntArgRegs[i] == reg) 1.268 + return true; 1.269 + } 1.270 + 1.271 + return false; 1.272 +} 1.273 + 1.274 +void 1.275 +MacroAssemblerX64::callWithABI(Address fun, MoveOp::Type result) 1.276 +{ 1.277 + if (IsIntArgReg(fun.base)) { 1.278 + // Callee register may be clobbered for an argument. Move the callee to 1.279 + // r10, a volatile, non-argument register. 1.280 + moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), MoveOp::GENERAL); 1.281 + fun.base = r10; 1.282 + } 1.283 + 1.284 + JS_ASSERT(!IsIntArgReg(fun.base)); 1.285 + 1.286 + uint32_t stackAdjust; 1.287 + callWithABIPre(&stackAdjust); 1.288 + call(Operand(fun)); 1.289 + callWithABIPost(stackAdjust, result); 1.290 +} 1.291 + 1.292 +void 1.293 +MacroAssemblerX64::handleFailureWithHandler(void *handler) 1.294 +{ 1.295 + // Reserve space for exception information. 1.296 + subq(Imm32(sizeof(ResumeFromException)), rsp); 1.297 + movq(rsp, rax); 1.298 + 1.299 + // Ask for an exception handler. 1.300 + setupUnalignedABICall(1, rcx); 1.301 + passABIArg(rax); 1.302 + callWithABI(handler); 1.303 + 1.304 + JitCode *excTail = GetIonContext()->runtime->jitRuntime()->getExceptionTail(); 1.305 + jmp(excTail); 1.306 +} 1.307 + 1.308 +void 1.309 +MacroAssemblerX64::handleFailureWithHandlerTail() 1.310 +{ 1.311 + Label entryFrame; 1.312 + Label catch_; 1.313 + Label finally; 1.314 + Label return_; 1.315 + Label bailout; 1.316 + 1.317 + loadPtr(Address(rsp, offsetof(ResumeFromException, kind)), rax); 1.318 + branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); 1.319 + branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_CATCH), &catch_); 1.320 + branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FINALLY), &finally); 1.321 + branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_); 1.322 + branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); 1.323 + 1.324 + breakpoint(); // Invalid kind. 1.325 + 1.326 + // No exception handler. Load the error value, load the new stack pointer 1.327 + // and return from the entry frame. 1.328 + bind(&entryFrame); 1.329 + moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); 1.330 + loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); 1.331 + ret(); 1.332 + 1.333 + // If we found a catch handler, this must be a baseline frame. Restore state 1.334 + // and jump to the catch block. 1.335 + bind(&catch_); 1.336 + loadPtr(Address(rsp, offsetof(ResumeFromException, target)), rax); 1.337 + loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp); 1.338 + loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); 1.339 + jmp(Operand(rax)); 1.340 + 1.341 + // If we found a finally block, this must be a baseline frame. Push 1.342 + // two values expected by JSOP_RETSUB: BooleanValue(true) and the 1.343 + // exception. 1.344 + bind(&finally); 1.345 + ValueOperand exception = ValueOperand(rcx); 1.346 + loadValue(Address(esp, offsetof(ResumeFromException, exception)), exception); 1.347 + 1.348 + loadPtr(Address(rsp, offsetof(ResumeFromException, target)), rax); 1.349 + loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp); 1.350 + loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); 1.351 + 1.352 + pushValue(BooleanValue(true)); 1.353 + pushValue(exception); 1.354 + jmp(Operand(rax)); 1.355 + 1.356 + // Only used in debug mode. Return BaselineFrame->returnValue() to the caller. 1.357 + bind(&return_); 1.358 + loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp); 1.359 + loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); 1.360 + loadValue(Address(rbp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); 1.361 + movq(rbp, rsp); 1.362 + pop(rbp); 1.363 + ret(); 1.364 + 1.365 + // If we are bailing out to baseline to handle an exception, jump to 1.366 + // the bailout tail stub. 1.367 + bind(&bailout); 1.368 + loadPtr(Address(esp, offsetof(ResumeFromException, bailoutInfo)), r9); 1.369 + mov(ImmWord(BAILOUT_RETURN_OK), rax); 1.370 + jmp(Operand(rsp, offsetof(ResumeFromException, target))); 1.371 +} 1.372 + 1.373 +#ifdef JSGC_GENERATIONAL 1.374 + 1.375 +void 1.376 +MacroAssemblerX64::branchPtrInNurseryRange(Register ptr, Register temp, Label *label) 1.377 +{ 1.378 + JS_ASSERT(ptr != temp); 1.379 + JS_ASSERT(ptr != ScratchReg); 1.380 + 1.381 + const Nursery &nursery = GetIonContext()->runtime->gcNursery(); 1.382 + movePtr(ImmWord(-ptrdiff_t(nursery.start())), ScratchReg); 1.383 + addPtr(ptr, ScratchReg); 1.384 + branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label); 1.385 +} 1.386 + 1.387 +void 1.388 +MacroAssemblerX64::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label) 1.389 +{ 1.390 + // 'Value' representing the start of the nursery tagged as a JSObject 1.391 + const Nursery &nursery = GetIonContext()->runtime->gcNursery(); 1.392 + Value start = ObjectValue(*reinterpret_cast<JSObject *>(nursery.start())); 1.393 + 1.394 + movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), ScratchReg); 1.395 + addPtr(value.valueReg(), ScratchReg); 1.396 + branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label); 1.397 +} 1.398 + 1.399 +#endif