From 59f1d2895ef5fc578b6d0c5137ae3124d24126ee Mon Sep 17 00:00:00 2001 From: Branislav Rankov Date: Thu, 5 Jun 2014 12:56:57 +0200 Subject: [PATCH] Bug 1001346 - IonMonkey MIPS: Adding MIPS OdinMonkey code part 2 (shared code). r=luke --- js/src/jit/AsmJS.cpp | 139 +++++++++++++++++++---- js/src/jit/AsmJSLink.cpp | 18 ++- js/src/jit/AsmJSModule.cpp | 14 ++- js/src/jit/AsmJSModule.h | 40 ++++++- js/src/jit/AsmJSSignalHandlers.cpp | 39 ++++++- js/src/jit/arm/Architecture-arm.cpp | 4 +- js/src/jit/arm/Assembler-arm.h | 9 ++ js/src/jit/mips/Architecture-mips.h | 2 +- js/src/jit/mips/Assembler-mips.cpp | 13 +++ js/src/jit/mips/Assembler-mips.h | 6 +- js/src/jit/shared/Assembler-shared.h | 2 +- js/src/jit/shared/Assembler-x86-shared.h | 8 ++ 12 files changed, 257 insertions(+), 37 deletions(-) diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index 28b5f1a6539d..e89abf8c8fb2 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1574,12 +1574,14 @@ class MOZ_STACK_CLASS ModuleCompiler // instruction. while (labelOffset != LabelBase::INVALID_OFFSET) { size_t patchAtOffset = masm_.labelOffsetToPatchOffset(labelOffset); - AsmJSModule::RelativeLink link; + AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::CodeLabel); link.patchAtOffset = patchAtOffset; link.targetOffset = targetOffset; if (!module_->addRelativeLink(link)) return false; - labelOffset = *(uintptr_t *)(module_->codeBase() + patchAtOffset); + + labelOffset = Assembler::extractCodeLabelOffset(module_->codeBase() + + patchAtOffset); } } @@ -1588,7 +1590,7 @@ class MOZ_STACK_CLASS ModuleCompiler FuncPtrTable &table = funcPtrTables_[tableIndex]; unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset(); for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) { - AsmJSModule::RelativeLink link; + AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::RawPointer); link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*); link.targetOffset = masm_.actualOffset(table.elem(elemIndex).code()->offset()); if (!module_->addRelativeLink(link)) @@ -1602,7 +1604,7 @@ class MOZ_STACK_CLASS ModuleCompiler // code section so we can just use an RelativeLink. for (unsigned i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) { AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i); - AsmJSModule::RelativeLink link; + AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::InstructionImmediate); link.patchAtOffset = masm_.labelOffsetToPatchOffset(a.patchAt.offset()); link.targetOffset = module_->offsetOfGlobalData() + a.globalDataOffset; if (!module_->addRelativeLink(link)) @@ -1620,6 +1622,24 @@ class MOZ_STACK_CLASS ModuleCompiler } #endif +#if defined(JS_CODEGEN_MIPS) + // On MIPS we need to update all the long jumps because they contain an + // absolute adress. + for (size_t i = 0; i < masm_.numLongJumps(); i++) { + uint32_t patchAtOffset = masm_.longJump(i); + + AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::InstructionImmediate); + link.patchAtOffset = patchAtOffset; + + InstImm *inst = (InstImm *)(module_->codeBase() + patchAtOffset); + link.targetOffset = Assembler::extractLuiOriValue(inst, inst->next()) - + (uint32_t)module_->codeBase(); + + if (!module_->addRelativeLink(link)) + return false; + } +#endif + // Absolute links for (size_t i = 0; i < masm_.numAsmJSAbsoluteLinks(); i++) { AsmJSAbsoluteLink src = masm_.asmJSAbsoluteLink(i); @@ -6055,14 +6075,22 @@ StackDecrementForCall(MacroAssembler &masm, const VectorT &argTypes, unsigned ex return StackDecrementForCall(masm, StackArgBytes(argTypes) + extraBytes); } +#if defined(JS_CODEGEN_MIPS) +// Mips is using one more double slot due to stack alignment for double values. +// Look at MacroAssembler::PushRegsInMask(RegisterSet set) +static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) + + NonVolatileRegs.fpus().size() * sizeof(double) + + sizeof(double); +#else static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) + NonVolatileRegs.fpus().size() * sizeof(double); +#endif -// On arm, we need to include an extra word of space at the top of the stack so -// we can explicitly store the return address before making the call to C++ or -// Ion. On x86/x64, this isn't necessary since the call instruction pushes the -// return address. -#ifdef JS_CODEGEN_ARM +// On ARM/MIPS, we need to include an extra word of space at the top of the +// stack so we can explicitly store the return address before making the call +// to C++ or Ion. On x86/x64, this isn't necessary since the call instruction +// pushes the return address. +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) static const unsigned MaybeRetAddr = sizeof(void*); #else static const unsigned MaybeRetAddr = 0; @@ -6087,6 +6115,9 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu // pop. masm.push(lr); #endif // JS_CODEGEN_ARM +#if defined(JS_CODEGEN_MIPS) + masm.push(ra); +#endif masm.PushRegsInMask(NonVolatileRegs); JS_ASSERT(masm.framePushed() == FramePushedAfterSave); @@ -6099,17 +6130,17 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu LoadAsmJSActivationIntoRegister(masm, activation); masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfErrorRejoinSP())); - // ARM has a globally-pinned GlobalReg (x64 uses RIP-relative addressing, - // x86 uses immediates in effective addresses) and NaN register (used as - // part of the out-of-bounds handling in heap loads/stores). -#if defined(JS_CODEGEN_ARM) + // ARM and MIPS have a globally-pinned GlobalReg (x64 uses RIP-relative + // addressing, x86 uses immediates in effective addresses) and NaN register + // (used as part of the out-of-bounds handling in heap loads/stores). +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) masm.movePtr(IntArgReg1, GlobalReg); - masm.ma_vimm(GenericNaN(), NANReg); + masm.loadConstantDouble(GenericNaN(), NANReg); #endif - // ARM and x64 have a globally-pinned HeapReg (x86 uses immediates in + // ARM, MIPS and x64 have a globally-pinned HeapReg (x86 uses immediates in // effective addresses). -#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) masm.loadPtr(Address(IntArgReg1, m.module().heapOffset()), HeapReg); #endif @@ -6345,6 +6376,9 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript // pop. masm.push(lr); #endif +#if defined(JS_CODEGEN_MIPS) + masm.push(ra); +#endif MIRType typeArray[] = { MIRType_Pointer, // cx MIRType_Pointer, // exitDatum @@ -6353,15 +6387,17 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript MIRTypeVector invokeArgTypes(m.cx()); invokeArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray)); - // The stack layout looks like: - // | return address | stack arguments | array of values | - unsigned arraySize = Max(1, exit.sig().args().length()) * sizeof(Value); - unsigned stackDec = StackDecrementForCall(masm, invokeArgTypes, arraySize + MaybeRetAddr); + // At the point of the call, the stack layout shall be (sp grows to the left): + // | retaddr | stack args | padding | Value argv[] | padding | retaddr | caller stack args | + // The first padding ensures double-alignment of argv; the second ensures + // sp is aligned. + unsigned offsetToArgv = AlignBytes(StackArgBytes(invokeArgTypes) + MaybeRetAddr, StackAlignment); + unsigned argvBytes = Max(1, exit.sig().args().length()) * sizeof(Value); + unsigned stackDec = StackDecrementForCall(masm, offsetToArgv + argvBytes); masm.reserveStack(stackDec); // Fill the argument array. unsigned offsetToCallerStackArgs = AlignmentAtAsmJSPrologue + masm.framePushed(); - unsigned offsetToArgv = StackArgBytes(invokeArgTypes) + MaybeRetAddr; Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0; FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch); @@ -6523,6 +6559,9 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit // The NANReg also needs to be restored, but is a constant and is reloaded before // returning to asm.js code. masm.PushRegsInMask(GeneralRegisterSet((1<cx(); @@ -6719,8 +6766,8 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit #if defined(JS_CODEGEN_X64) masm.Pop(HeapReg); #endif -#if defined(JS_CODEGEN_ARM) - masm.ma_vimm(GenericNaN(), NANReg); +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) + masm.loadConstantDouble(GenericNaN(), NANReg); masm.PopRegsInMask(GeneralRegisterSet((1<> ARCH_BITS)); *cpuId = ARM | (GetARMFlags() << ARCH_BITS); return true; +#elif defined(JS_CODEGEN_MIPS) + JS_ASSERT(GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS)); + *cpuId = MIPS | (GetMIPSFlags() << ARCH_BITS); + return true; #else return false; #endif diff --git a/js/src/jit/AsmJSModule.h b/js/src/jit/AsmJSModule.h index 523c18f4a539..dcc52b16674c 100644 --- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -370,6 +370,39 @@ class AsmJSModule struct RelativeLink { + enum Kind + { + RawPointer, + CodeLabel, + InstructionImmediate + }; + + RelativeLink() + { } + + RelativeLink(Kind kind) + { +#if defined(JS_CODEGEN_MIPS) + kind_ = kind; +#elif defined(JS_CODEGEN_ARM) + // On ARM, CodeLabels are only used to label raw pointers, so in + // all cases on ARM, a RelativePatch means patching a raw pointer. + JS_ASSERT(kind == CodeLabel || kind == RawPointer); +#endif + // On X64 and X86, all RelativePatch-es are patched as raw pointers. + } + + bool isRawPointerPatch() { +#if defined(JS_CODEGEN_MIPS) + return kind_ == RawPointer; +#else + return true; +#endif + } + +#ifdef JS_CODEGEN_MIPS + Kind kind_; +#endif uint32_t patchAtOffset; uint32_t targetOffset; }; @@ -727,7 +760,8 @@ class AsmJSModule // The global data section is placed after the executable code (i.e., at // offset codeBytes_) in the module's linear allocation. The global data // are laid out in this order: - // 0. a pointer/descriptor for the heap that was linked to the module + // 0. a pointer (padded up to 8 bytes to ensure double-alignment of + // globals) for the heap that was linked to the module. // 1. global variable state (elements are sizeof(uint64_t)) // 2. interleaved function-pointer tables and exits. These are allocated // while type checking function bodies (as exits and uses of @@ -740,7 +774,7 @@ class AsmJSModule return code_ + offsetOfGlobalData(); } size_t globalDataBytes() const { - return sizeof(void*) + + return sizeof(uint64_t) + pod.numGlobalVars_ * sizeof(uint64_t) + pod.funcPtrTableAndExitBytes_; } @@ -752,7 +786,7 @@ class AsmJSModule } unsigned globalVarIndexToGlobalDataOffset(unsigned i) const { JS_ASSERT(i < pod.numGlobalVars_); - return sizeof(void*) + + return sizeof(uint64_t) + i * sizeof(uint64_t); } void *globalVarIndexToGlobalDatum(unsigned i) const { diff --git a/js/src/jit/AsmJSSignalHandlers.cpp b/js/src/jit/AsmJSSignalHandlers.cpp index 8846d58680ec..cf9dc728a195 100644 --- a/js/src/jit/AsmJSSignalHandlers.cpp +++ b/js/src/jit/AsmJSSignalHandlers.cpp @@ -86,6 +86,11 @@ using JS::GenericNaN; # else # define R15_sig(p) ((p)->uc_mcontext.gregs[REG_R15]) # endif +# if defined(__linux__) && defined(__mips__) +# define EPC_sig(p) ((p)->uc_mcontext.pc) +# define RSP_sig(p) ((p)->uc_mcontext.gregs[29]) +# define RFP_sig(p) ((p)->uc_mcontext.gregs[30]) +# endif #elif defined(__NetBSD__) # define XMM_sig(p,i) (((struct fxsave64 *)(p)->uc_mcontext.__fpregs)->fx_xmm[i]) # define EIP_sig(p) ((p)->uc_mcontext.__gregs[_REG_EIP]) @@ -282,6 +287,37 @@ typedef struct ucontext { // Other fields are not used so don't define them here. } ucontext_t; +# elif defined(__mips__) + +typedef struct { + uint32_t regmask; + uint32_t status; + uint64_t pc; + uint64_t gregs[32]; + uint64_t fpregs[32]; + uint32_t acx; + uint32_t fpc_csr; + uint32_t fpc_eir; + uint32_t used_math; + uint32_t dsp; + uint64_t mdhi; + uint64_t mdlo; + uint32_t hi1; + uint32_t lo1; + uint32_t hi2; + uint32_t lo2; + uint32_t hi3; + uint32_t lo3; +} mcontext_t; + +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + // Other fields are not used so don't define them here. +} ucontext_t; + # elif defined(__i386__) // x86 version for Android. typedef struct { @@ -326,6 +362,8 @@ static bool IsSignalHandlingBroken() { return false; } # define PC_sig(p) EIP_sig(p) #elif defined(JS_CPU_ARM) # define PC_sig(p) R15_sig(p) +#elif defined(JS_CPU_MIPS) +# define PC_sig(p) EPC_sig(p) #endif static bool @@ -355,7 +393,6 @@ HandleSimulatorInterrupt(JSRuntime *rt, AsmJSActivation *activation, void *fault static uint8_t ** ContextToPC(CONTEXT *context) { - JS_STATIC_ASSERT(sizeof(PC_sig(context)) == sizeof(void*)); return reinterpret_cast(&PC_sig(context)); } diff --git a/js/src/jit/arm/Architecture-arm.cpp b/js/src/jit/arm/Architecture-arm.cpp index f56c031f9b6c..fabc63f3ad05 100644 --- a/js/src/jit/arm/Architecture-arm.cpp +++ b/js/src/jit/arm/Architecture-arm.cpp @@ -15,10 +15,10 @@ #include "jit/arm/Assembler-arm.h" -#define HWCAP_USE_HARDFP_ABI (1 << 28) +#define HWCAP_USE_HARDFP_ABI (1 << 27) #if !(defined(ANDROID) || defined(MOZ_B2G)) && !defined(JS_ARM_SIMULATOR) -#define HWCAP_ARMv7 (1 << 29) +#define HWCAP_ARMv7 (1 << 28) #include #else #define HWCAP_VFP (1<<0) diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 154c5686cb4a..b9d50413d72a 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1798,6 +1798,11 @@ class Assembler : public AssemblerShared static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue); static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm); + + static void patchInstructionImmediate(uint8_t *code, PatchedImmPtr imm) { + MOZ_ASSUME_UNREACHABLE("Unused."); + } + static uint32_t alignDoubleArg(uint32_t offset) { return (offset+1)&~1; } @@ -1811,6 +1816,10 @@ class Assembler : public AssemblerShared static void updateBoundsCheck(uint32_t logHeapSize, Instruction *inst); void processCodeLabels(uint8_t *rawCode); + static int32_t extractCodeLabelOffset(uint8_t *code) { + return *(uintptr_t *)code; + } + bool bailed() { return m_buffer.bail(); } diff --git a/js/src/jit/mips/Architecture-mips.h b/js/src/jit/mips/Architecture-mips.h index 4042eae504d9..b34c4575e808 100644 --- a/js/src/jit/mips/Architecture-mips.h +++ b/js/src/jit/mips/Architecture-mips.h @@ -28,7 +28,7 @@ namespace js { namespace jit { // Shadow stack space is not required on MIPS. -static const uint32_t ShadowStackSpace = 0; +static const uint32_t ShadowStackSpace = 4 * sizeof(uintptr_t); // These offsets are specific to nunboxing, and capture offsets into the // components of a js::Value. diff --git a/js/src/jit/mips/Assembler-mips.cpp b/js/src/jit/mips/Assembler-mips.cpp index 1a51f8de13a6..9134dfa5b717 100644 --- a/js/src/jit/mips/Assembler-mips.cpp +++ b/js/src/jit/mips/Assembler-mips.cpp @@ -329,6 +329,12 @@ Assembler::processCodeLabels(uint8_t *rawCode) } } +int32_t +Assembler::extractCodeLabelOffset(uint8_t *code) { + InstImm *inst = (InstImm *)code; + return Assembler::extractLuiOriValue(inst, inst->next()); +} + void Assembler::Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address) { @@ -1450,6 +1456,13 @@ Assembler::patchWrite_Imm32(CodeLocationLabel label, Imm32 imm) *(raw - 1) = imm.value; } +void +Assembler::patchInstructionImmediate(uint8_t *code, PatchedImmPtr imm) +{ + InstImm *inst = (InstImm *)code; + Assembler::updateLuiOriValue(inst, inst->next(), (uint32_t)imm.value); +} + uint8_t * Assembler::nextInstruction(uint8_t *inst_, uint32_t *count) { diff --git a/js/src/jit/mips/Assembler-mips.h b/js/src/jit/mips/Assembler-mips.h index b3753f8f8d0f..7754a4cd7d39 100644 --- a/js/src/jit/mips/Assembler-mips.h +++ b/js/src/jit/mips/Assembler-mips.h @@ -89,7 +89,7 @@ class ABIArgGenerator uint32_t stackBytesConsumedSoFar() const { if (usedArgSlots_ <= 4) - return 4 * sizeof(intptr_t); + return ShadowStackSpace; return usedArgSlots_ * sizeof(intptr_t); } @@ -1033,6 +1033,9 @@ class Assembler : public AssemblerShared static void patchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ImmPtr expectedValue); static void patchWrite_Imm32(CodeLocationLabel label, Imm32 imm); + + static void patchInstructionImmediate(uint8_t *code, PatchedImmPtr imm); + static uint32_t alignDoubleArg(uint32_t offset) { return (offset + 1U) &~ 1U; } @@ -1046,6 +1049,7 @@ class Assembler : public AssemblerShared static void updateBoundsCheck(uint32_t logHeapSize, Instruction *inst); void processCodeLabels(uint8_t *rawCode); + static int32_t extractCodeLabelOffset(uint8_t *code); bool bailed() { return m_buffer.bail(); diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index ebdb148217b7..8de0726161c6 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -691,7 +691,7 @@ class AsmJSHeapAccess isFloat32Load_(false), loadedReg_(UINT8_MAX) {} -#elif defined(JS_CODEGEN_ARM) +#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS) explicit AsmJSHeapAccess(uint32_t offset) : offset_(offset) {} diff --git a/js/src/jit/shared/Assembler-x86-shared.h b/js/src/jit/shared/Assembler-x86-shared.h index 58c273689dd9..1001f07504c6 100644 --- a/js/src/jit/shared/Assembler-x86-shared.h +++ b/js/src/jit/shared/Assembler-x86-shared.h @@ -288,6 +288,9 @@ class AssemblerX86Shared : public AssemblerShared void executableCopy(void *buffer); void processCodeLabels(uint8_t *rawCode); + static int32_t extractCodeLabelOffset(uint8_t *code) { + return *(uintptr_t *)code; + } void copyJumpRelocationTable(uint8_t *dest); void copyDataRelocationTable(uint8_t *dest); void copyPreBarrierTable(uint8_t *dest); @@ -1677,6 +1680,11 @@ class AssemblerX86Shared : public AssemblerShared static void patchDataWithValueCheck(CodeLocationLabel data, ImmPtr newData, ImmPtr expectedData) { patchDataWithValueCheck(data, PatchedImmPtr(newData.value), PatchedImmPtr(expectedData.value)); } + + static void patchInstructionImmediate(uint8_t *code, PatchedImmPtr imm) { + MOZ_ASSUME_UNREACHABLE("Unused."); + } + static uint32_t nopSize() { return 1; }