1/* 2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "RegisterPreservationWrapperGenerator.h" 28 29#if ENABLE(JIT) 30 31#include "AssemblyHelpers.h" 32#include "LinkBuffer.h" 33#include "JSCInlines.h" 34#include "StackAlignment.h" 35 36namespace JSC { 37 38RegisterSet registersToPreserve() 39{ 40 RegisterSet calleeSaves = RegisterSet::calleeSaveRegisters(); 41 42 // No need to preserve FP since that always gets preserved anyway. 43 calleeSaves.clear(GPRInfo::callFrameRegister); 44 45 return calleeSaves; 46} 47 48ptrdiff_t registerPreservationOffset() 49{ 50 unsigned numberOfCalleeSaves = registersToPreserve().numberOfSetRegisters(); 51 52 // Need to preserve the old return PC. 53 unsigned numberOfValuesToSave = numberOfCalleeSaves + 1; 54 55 // Alignment. Preserve the same alignment invariants that the caller imposed. 56 unsigned numberOfNewStackSlots = 57 WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numberOfValuesToSave); 58 59 return sizeof(Register) * numberOfNewStackSlots; 60} 61 62MacroAssemblerCodeRef generateRegisterPreservationWrapper(VM& vm, ExecutableBase* executable, MacroAssemblerCodePtr target) 63{ 64#if ENABLE(FTL_JIT) 65 // We shouldn't ever be generating wrappers for native functions. 66 RegisterSet toSave = registersToPreserve(); 67 ptrdiff_t offset = registerPreservationOffset(); 68 69 AssemblyHelpers jit(&vm, 0); 70 71 jit.preserveReturnAddressAfterCall(GPRInfo::regT1); 72 jit.load32( 73 AssemblyHelpers::Address( 74 AssemblyHelpers::stackPointerRegister, 75 (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset), 76 GPRInfo::regT2); 77 78 // Place the stack pointer where we want it to be. 79 jit.subPtr(AssemblyHelpers::TrustedImm32(offset), AssemblyHelpers::stackPointerRegister); 80 81 // Compute the number of things we will be copying. 82 jit.add32( 83 AssemblyHelpers::TrustedImm32( 84 JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize), 85 GPRInfo::regT2); 86 87 ASSERT(!toSave.get(GPRInfo::regT4)); 88 jit.move(AssemblyHelpers::stackPointerRegister, GPRInfo::regT4); 89 90 AssemblyHelpers::Label loop = jit.label(); 91 jit.sub32(AssemblyHelpers::TrustedImm32(1), GPRInfo::regT2); 92 jit.load64(AssemblyHelpers::Address(GPRInfo::regT4, offset), GPRInfo::regT0); 93 jit.store64(GPRInfo::regT0, GPRInfo::regT4); 94 jit.addPtr(AssemblyHelpers::TrustedImm32(sizeof(Register)), GPRInfo::regT4); 95 jit.branchTest32(AssemblyHelpers::NonZero, GPRInfo::regT2).linkTo(loop, &jit); 96 97 // At this point regT4 + offset points to where we save things. 98 ptrdiff_t currentOffset = 0; 99 jit.storePtr(GPRInfo::regT1, AssemblyHelpers::Address(GPRInfo::regT4, currentOffset)); 100 101 for (GPRReg gpr = AssemblyHelpers::firstRegister(); gpr <= AssemblyHelpers::lastRegister(); gpr = static_cast<GPRReg>(gpr + 1)) { 102 if (!toSave.get(gpr)) 103 continue; 104 currentOffset += sizeof(Register); 105 jit.store64(gpr, AssemblyHelpers::Address(GPRInfo::regT4, currentOffset)); 106 } 107 for (FPRReg fpr = AssemblyHelpers::firstFPRegister(); fpr <= AssemblyHelpers::lastFPRegister(); fpr = static_cast<FPRReg>(fpr + 1)) { 108 if (!toSave.get(fpr)) 109 continue; 110 currentOffset += sizeof(Register); 111 jit.storeDouble(fpr, AssemblyHelpers::Address(GPRInfo::regT4, currentOffset)); 112 } 113 114 // Assume that there aren't any saved FP registers. 115 116 // Restore the tag registers. 117 jit.move(AssemblyHelpers::TrustedImm64(TagTypeNumber), GPRInfo::tagTypeNumberRegister); 118 jit.add64(AssemblyHelpers::TrustedImm32(TagMask - TagTypeNumber), GPRInfo::tagTypeNumberRegister, GPRInfo::tagMaskRegister); 119 120 jit.move( 121 AssemblyHelpers::TrustedImmPtr( 122 vm.getCTIStub(registerRestorationThunkGenerator).code().executableAddress()), 123 GPRInfo::nonArgGPR0); 124 jit.restoreReturnAddressBeforeReturn(GPRInfo::nonArgGPR0); 125 AssemblyHelpers::Jump jump = jit.jump(); 126 127 LinkBuffer linkBuffer(vm, jit, GLOBAL_THUNK_ID); 128 linkBuffer.link(jump, CodeLocationLabel(target)); 129 130 if (Options::verboseFTLToJSThunk()) 131 dataLog("Need a thunk for calls from FTL to non-FTL version of ", *executable, "\n"); 132 133 return FINALIZE_DFG_CODE(linkBuffer, ("Register preservation wrapper for %s/%s, %p", toCString(executable->hashFor(CodeForCall)).data(), toCString(executable->hashFor(CodeForConstruct)).data(), target.executableAddress())); 134#else // ENABLE(FTL_JIT) 135 UNUSED_PARAM(vm); 136 UNUSED_PARAM(executable); 137 UNUSED_PARAM(target); 138 // We don't support non-FTL builds for two reasons: 139 // - It just so happens that currently only the FTL bottoms out in this code. 140 // - The code above uses 64-bit instructions. It doesn't necessarily have to; it would be 141 // easy to change it so that it doesn't. But obviously making that change would be a 142 // prerequisite to removing this #if. 143 UNREACHABLE_FOR_PLATFORM(); 144 return MacroAssemblerCodeRef(); 145#endif // ENABLE(FTL_JIT) 146} 147 148static void generateRegisterRestoration(AssemblyHelpers& jit) 149{ 150#if ENABLE(FTL_JIT) 151 RegisterSet toSave = registersToPreserve(); 152 ptrdiff_t offset = registerPreservationOffset(); 153 154 ASSERT(!toSave.get(GPRInfo::regT4)); 155 156 // We need to place the stack pointer back to where the caller thought they left it. 157 // But also, in order to recover the registers, we need to figure out how big the 158 // arguments area is. 159 160 jit.load32( 161 AssemblyHelpers::Address( 162 AssemblyHelpers::stackPointerRegister, 163 (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset), 164 GPRInfo::regT4); 165 166 jit.move(GPRInfo::regT4, GPRInfo::regT2); 167 jit.lshift32(AssemblyHelpers::TrustedImm32(3), GPRInfo::regT2); 168 169 jit.addPtr(AssemblyHelpers::TrustedImm32(offset), AssemblyHelpers::stackPointerRegister); 170 jit.addPtr(AssemblyHelpers::stackPointerRegister, GPRInfo::regT2); 171 172 // We saved things at: 173 // 174 // adjSP + (JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize + NumArgs) * 8 175 // 176 // Where: 177 // 178 // adjSP = origSP - offset 179 // 180 // regT2 now points at: 181 // 182 // origSP + NumArgs * 8 183 // = adjSP + offset + NumArgs * 8 184 // 185 // So if we subtract offset and then add JSStack::CallFrameHeaderSize and subtract 186 // JSStack::CallerFrameAndPCSize, we'll get the thing we want. 187 ptrdiff_t currentOffset = -offset + sizeof(Register) * ( 188 JSStack::CallFrameHeaderSize - JSStack::CallerFrameAndPCSize); 189 jit.loadPtr(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), GPRInfo::regT1); 190 191 for (GPRReg gpr = AssemblyHelpers::firstRegister(); gpr <= AssemblyHelpers::lastRegister(); gpr = static_cast<GPRReg>(gpr + 1)) { 192 if (!toSave.get(gpr)) 193 continue; 194 currentOffset += sizeof(Register); 195 jit.load64(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), gpr); 196 } 197 for (FPRReg fpr = AssemblyHelpers::firstFPRegister(); fpr <= AssemblyHelpers::lastFPRegister(); fpr = static_cast<FPRReg>(fpr + 1)) { 198 if (!toSave.get(fpr)) 199 continue; 200 currentOffset += sizeof(Register); 201 jit.loadDouble(AssemblyHelpers::Address(GPRInfo::regT2, currentOffset), fpr); 202 } 203 204 // Thunks like this rely on the ArgumentCount being intact. Pay it forward. 205 jit.store32( 206 GPRInfo::regT4, 207 AssemblyHelpers::Address( 208 AssemblyHelpers::stackPointerRegister, 209 (JSStack::ArgumentCount - JSStack::CallerFrameAndPCSize) * sizeof(Register) + PayloadOffset)); 210 211 if (!ASSERT_DISABLED) { 212 AssemblyHelpers::Jump ok = jit.branchPtr( 213 AssemblyHelpers::Above, GPRInfo::regT1, AssemblyHelpers::TrustedImmPtr(static_cast<size_t>(0x1000))); 214 jit.abortWithReason(RPWUnreasonableJumpTarget); 215 ok.link(&jit); 216 } 217 218 jit.jump(GPRInfo::regT1); 219#else // ENABLE(FTL_JIT) 220 UNUSED_PARAM(jit); 221 UNREACHABLE_FOR_PLATFORM(); 222#endif // ENABLE(FTL_JIT) 223} 224 225MacroAssemblerCodeRef registerRestorationThunkGenerator(VM* vm) 226{ 227 AssemblyHelpers jit(vm, 0); 228 generateRegisterRestoration(jit); 229 LinkBuffer linkBuffer(*vm, jit, GLOBAL_THUNK_ID); 230 return FINALIZE_CODE(linkBuffer, ("Register restoration thunk")); 231} 232 233} // namespace JSC 234 235#endif // ENABLE(JIT) 236 237