LinkBuffer.h
1 /* 2 * Copyright (C) 2009-2018 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 #pragma once 27 28 #if ENABLE(ASSEMBLER) 29 30 #define DUMP_LINK_STATISTICS 0 31 #define DUMP_CODE 0 32 33 #define GLOBAL_THUNK_ID reinterpret_cast<void*>(static_cast<intptr_t>(-1)) 34 #define REGEXP_CODE_ID reinterpret_cast<void*>(static_cast<intptr_t>(-2)) 35 #define CSS_CODE_ID reinterpret_cast<void*>(static_cast<intptr_t>(-3)) 36 37 #include "JITCompilationEffort.h" 38 #include "MacroAssembler.h" 39 #include "MacroAssemblerCodeRef.h" 40 #include <wtf/DataLog.h> 41 #include <wtf/FastMalloc.h> 42 #include <wtf/Noncopyable.h> 43 44 namespace JSC { 45 46 namespace Wasm { 47 enum class CompilationMode : uint8_t; 48 } 49 50 class CodeBlock; 51 52 // LinkBuffer: 53 // 54 // This class assists in linking code generated by the macro assembler, once code generation 55 // has been completed, and the code has been copied to is final location in memory. At this 56 // time pointers to labels within the code may be resolved, and relative offsets to external 57 // addresses may be fixed. 58 // 59 // Specifically: 60 // * Jump objects may be linked to external targets, 61 // * The address of Jump objects may taken, such that it can later be relinked. 62 // * The return address of a Call may be acquired. 63 // * The address of a Label pointing into the code may be resolved. 64 // * The value referenced by a DataLabel may be set. 65 // 66 class LinkBuffer { 67 WTF_MAKE_NONCOPYABLE(LinkBuffer); WTF_MAKE_FAST_ALLOCATED; 68 69 template<PtrTag tag> using CodePtr = MacroAssemblerCodePtr<tag>; 70 template<PtrTag tag> using CodeRef = MacroAssemblerCodeRef<tag>; 71 typedef MacroAssembler::Label Label; 72 typedef MacroAssembler::Jump Jump; 73 typedef MacroAssembler::PatchableJump PatchableJump; 74 typedef MacroAssembler::JumpList JumpList; 75 typedef MacroAssembler::Call Call; 76 typedef MacroAssembler::DataLabelCompact DataLabelCompact; 77 typedef MacroAssembler::DataLabel32 DataLabel32; 78 typedef MacroAssembler::DataLabelPtr DataLabelPtr; 79 typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; 80 #if ENABLE(BRANCH_COMPACTION) 81 typedef MacroAssembler::LinkRecord LinkRecord; 82 typedef MacroAssembler::JumpLinkType JumpLinkType; 83 #endif 84 85 public: 86 LinkBuffer(MacroAssembler& macroAssembler, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) 87 : m_size(0) 88 , m_didAllocate(false) 89 #ifndef NDEBUG 90 , m_completed(false) 91 #endif 92 { 93 UNUSED_PARAM(ownerUID); 94 linkCode(macroAssembler, effort); 95 } 96 97 template<PtrTag tag> 98 LinkBuffer(MacroAssembler& macroAssembler, MacroAssemblerCodePtr<tag> code, size_t size, JITCompilationEffort effort = JITCompilationMustSucceed, bool shouldPerformBranchCompaction = true) 99 : m_size(size) 100 , m_didAllocate(false) 101 #ifndef NDEBUG 102 , m_completed(false) 103 #endif 104 , m_code(code.template retagged<LinkBufferPtrTag>()) 105 { 106 #if ENABLE(BRANCH_COMPACTION) 107 m_shouldPerformBranchCompaction = shouldPerformBranchCompaction; 108 #else 109 UNUSED_PARAM(shouldPerformBranchCompaction); 110 #endif 111 linkCode(macroAssembler, effort); 112 } 113 114 ~LinkBuffer() 115 { 116 } 117 118 bool didFailToAllocate() const 119 { 120 return !m_didAllocate; 121 } 122 123 bool isValid() const 124 { 125 return !didFailToAllocate(); 126 } 127 128 void setIsJumpIsland() 129 { 130 #if ASSERT_ENABLED 131 m_isJumpIsland = true; 132 #endif 133 } 134 135 // These methods are used to link or set values at code generation time. 136 137 template<PtrTag tag, typename Func, typename = std::enable_if_t<std::is_function<typename std::remove_pointer<Func>::type>::value>> 138 void link(Call call, Func funcName) 139 { 140 FunctionPtr<tag> function(funcName); 141 link(call, function); 142 } 143 144 template<PtrTag tag> 145 void link(Call call, FunctionPtr<tag> function) 146 { 147 ASSERT(call.isFlagSet(Call::Linkable)); 148 call.m_label = applyOffset(call.m_label); 149 MacroAssembler::linkCall(code(), call, function); 150 } 151 152 template<PtrTag tag> 153 void link(Call call, CodeLocationLabel<tag> label) 154 { 155 link(call, FunctionPtr<tag>(label)); 156 } 157 158 template<PtrTag tag> 159 void link(Jump jump, CodeLocationLabel<tag> label) 160 { 161 jump.m_label = applyOffset(jump.m_label); 162 MacroAssembler::linkJump(code(), jump, label); 163 } 164 165 template<PtrTag tag> 166 void link(const JumpList& list, CodeLocationLabel<tag> label) 167 { 168 for (const Jump& jump : list.jumps()) 169 link(jump, label); 170 } 171 172 void patch(DataLabelPtr label, void* value) 173 { 174 AssemblerLabel target = applyOffset(label.m_label); 175 MacroAssembler::linkPointer(code(), target, value); 176 } 177 178 template<PtrTag tag> 179 void patch(DataLabelPtr label, CodeLocationLabel<tag> value) 180 { 181 AssemblerLabel target = applyOffset(label.m_label); 182 MacroAssembler::linkPointer(code(), target, value); 183 } 184 185 // These methods are used to obtain handles to allow the code to be relinked / repatched later. 186 187 template<PtrTag tag> 188 CodeLocationLabel<tag> entrypoint() 189 { 190 return CodeLocationLabel<tag>(tagCodePtr<tag>(code())); 191 } 192 193 template<PtrTag tag> 194 CodeLocationCall<tag> locationOf(Call call) 195 { 196 ASSERT(call.isFlagSet(Call::Linkable)); 197 ASSERT(!call.isFlagSet(Call::Near)); 198 return CodeLocationCall<tag>(getLinkerAddress<tag>(applyOffset(call.m_label))); 199 } 200 201 template<PtrTag tag> 202 CodeLocationNearCall<tag> locationOfNearCall(Call call) 203 { 204 ASSERT(call.isFlagSet(Call::Linkable)); 205 ASSERT(call.isFlagSet(Call::Near)); 206 return CodeLocationNearCall<tag>(getLinkerAddress<tag>(applyOffset(call.m_label)), 207 call.isFlagSet(Call::Tail) ? NearCallMode::Tail : NearCallMode::Regular); 208 } 209 210 template<PtrTag tag> 211 CodeLocationLabel<tag> locationOf(PatchableJump jump) 212 { 213 return CodeLocationLabel<tag>(getLinkerAddress<tag>(applyOffset(jump.m_jump.m_label))); 214 } 215 216 template<PtrTag tag> 217 CodeLocationLabel<tag> locationOf(Label label) 218 { 219 return CodeLocationLabel<tag>(getLinkerAddress<tag>(applyOffset(label.m_label))); 220 } 221 222 template<PtrTag tag> 223 CodeLocationDataLabelPtr<tag> locationOf(DataLabelPtr label) 224 { 225 return CodeLocationDataLabelPtr<tag>(getLinkerAddress<tag>(applyOffset(label.m_label))); 226 } 227 228 template<PtrTag tag> 229 CodeLocationDataLabel32<tag> locationOf(DataLabel32 label) 230 { 231 return CodeLocationDataLabel32<tag>(getLinkerAddress<tag>(applyOffset(label.m_label))); 232 } 233 234 template<PtrTag tag> 235 CodeLocationDataLabelCompact<tag> locationOf(DataLabelCompact label) 236 { 237 return CodeLocationDataLabelCompact<tag>(getLinkerAddress<tag>(applyOffset(label.m_label))); 238 } 239 240 template<PtrTag tag> 241 CodeLocationConvertibleLoad<tag> locationOf(ConvertibleLoadLabel label) 242 { 243 return CodeLocationConvertibleLoad<tag>(getLinkerAddress<tag>(applyOffset(label.m_label))); 244 } 245 246 // This method obtains the return address of the call, given as an offset from 247 // the start of the code. 248 unsigned returnAddressOffset(Call call) 249 { 250 call.m_label = applyOffset(call.m_label); 251 return MacroAssembler::getLinkerCallReturnOffset(call); 252 } 253 254 uint32_t offsetOf(Label label) 255 { 256 return applyOffset(label.m_label).offset(); 257 } 258 259 unsigned offsetOf(PatchableJump jump) 260 { 261 return applyOffset(jump.m_jump.m_label).offset(); 262 } 263 264 // Upon completion of all patching 'FINALIZE_CODE()' should be called once to 265 // complete generation of the code. Alternatively, call 266 // finalizeCodeWithoutDisassembly() directly if you have your own way of 267 // displaying disassembly. 268 269 template<PtrTag tag> 270 CodeRef<tag> finalizeCodeWithoutDisassembly() 271 { 272 return finalizeCodeWithoutDisassemblyImpl().template retagged<tag>(); 273 } 274 275 template<PtrTag tag, typename... Args> 276 CodeRef<tag> finalizeCodeWithDisassembly(bool dumpDisassembly, const char* format, Args... args) 277 { 278 ALLOW_NONLITERAL_FORMAT_BEGIN 279 IGNORE_WARNINGS_BEGIN("format-security") 280 return finalizeCodeWithDisassemblyImpl(dumpDisassembly, format, args...).template retagged<tag>(); 281 IGNORE_WARNINGS_END 282 ALLOW_NONLITERAL_FORMAT_END 283 } 284 285 template<PtrTag tag> 286 CodePtr<tag> trampolineAt(Label label) 287 { 288 return CodePtr<tag>(MacroAssembler::AssemblerType_T::getRelocatedAddress(code(), applyOffset(label.m_label))); 289 } 290 291 void* debugAddress() 292 { 293 return m_code.dataLocation(); 294 } 295 296 size_t size() const { return m_size; } 297 298 bool wasAlreadyDisassembled() const { return m_alreadyDisassembled; } 299 void didAlreadyDisassemble() { m_alreadyDisassembled = true; } 300 301 private: 302 JS_EXPORT_PRIVATE CodeRef<LinkBufferPtrTag> finalizeCodeWithoutDisassemblyImpl(); 303 JS_EXPORT_PRIVATE CodeRef<LinkBufferPtrTag> finalizeCodeWithDisassemblyImpl(bool dumpDisassembly, const char* format, ...) WTF_ATTRIBUTE_PRINTF(3, 4); 304 305 #if ENABLE(BRANCH_COMPACTION) 306 int executableOffsetFor(int location) 307 { 308 // Returning 0 in this case works because at location < 309 // sizeof(int32_t), no compaction could have happened before this 310 // point as the assembler could not have placed a branch instruction 311 // within this space that required compaction. 312 if (location < static_cast<int>(sizeof(int32_t))) 313 return 0; 314 return bitwise_cast<int32_t*>(m_assemblerStorage.buffer())[location / sizeof(int32_t) - 1]; 315 } 316 #endif 317 318 template <typename T> T applyOffset(T src) 319 { 320 #if ENABLE(BRANCH_COMPACTION) 321 src = src.labelAtOffset(-executableOffsetFor(src.offset())); 322 #endif 323 return src; 324 } 325 326 // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). 327 void* code() 328 { 329 return m_code.dataLocation(); 330 } 331 332 void allocate(MacroAssembler&, JITCompilationEffort); 333 334 template<PtrTag tag, typename T> 335 void* getLinkerAddress(T src) 336 { 337 void *code = this->code(); 338 void* address = MacroAssembler::getLinkerAddress<tag>(code, src); 339 RELEASE_ASSERT(code <= untagCodePtr<tag>(address) && untagCodePtr<tag>(address) <= static_cast<char*>(code) + size()); 340 return address; 341 } 342 343 JS_EXPORT_PRIVATE void linkCode(MacroAssembler&, JITCompilationEffort); 344 #if ENABLE(BRANCH_COMPACTION) 345 template <typename InstructionType> 346 void copyCompactAndLinkCode(MacroAssembler&, JITCompilationEffort); 347 #endif 348 349 void performFinalization(); 350 351 #if DUMP_LINK_STATISTICS 352 static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize); 353 #endif 354 355 #if DUMP_CODE 356 static void dumpCode(void* code, size_t); 357 #endif 358 359 RefPtr<ExecutableMemoryHandle> m_executableMemory; 360 size_t m_size; 361 #if ENABLE(BRANCH_COMPACTION) 362 AssemblerData m_assemblerStorage; 363 #if CPU(ARM64E) 364 AssemblerData m_assemblerHashesStorage; 365 #endif 366 bool m_shouldPerformBranchCompaction { true }; 367 #endif 368 bool m_didAllocate; 369 #ifndef NDEBUG 370 bool m_completed; 371 #endif 372 #if ASSERT_ENABLED 373 bool m_isJumpIsland { false }; 374 #endif 375 bool m_alreadyDisassembled { false }; 376 MacroAssemblerCodePtr<LinkBufferPtrTag> m_code; 377 Vector<RefPtr<SharedTask<void(LinkBuffer&)>>> m_linkTasks; 378 }; 379 380 #if OS(LINUX) 381 #define FINALIZE_CODE_IF(condition, linkBufferReference, resultPtrTag, ...) \ 382 (UNLIKELY((condition)) \ 383 ? (linkBufferReference).finalizeCodeWithDisassembly<resultPtrTag>(true, __VA_ARGS__) \ 384 : (UNLIKELY(JSC::Options::logJITCodeForPerf()) \ 385 ? (linkBufferReference).finalizeCodeWithDisassembly<resultPtrTag>(false, __VA_ARGS__) \ 386 : (linkBufferReference).finalizeCodeWithoutDisassembly<resultPtrTag>())) 387 #else 388 #define FINALIZE_CODE_IF(condition, linkBufferReference, resultPtrTag, ...) \ 389 (UNLIKELY((condition)) \ 390 ? (linkBufferReference).finalizeCodeWithDisassembly<resultPtrTag>(true, __VA_ARGS__) \ 391 : (linkBufferReference).finalizeCodeWithoutDisassembly<resultPtrTag>()) 392 #endif 393 394 bool shouldDumpDisassemblyFor(CodeBlock*); 395 396 #define FINALIZE_CODE_FOR(codeBlock, linkBufferReference, resultPtrTag, ...) \ 397 FINALIZE_CODE_IF((shouldDumpDisassemblyFor(codeBlock) || Options::asyncDisassembly()), linkBufferReference, resultPtrTag, __VA_ARGS__) 398 399 // Use this to finalize code, like so: 400 // 401 // CodeRef code = FINALIZE_CODE(linkBuffer, tag, "my super thingy number %d", number); 402 // 403 // Which, in disassembly mode, will print: 404 // 405 // Generated JIT code for my super thingy number 42: 406 // Code at [0x123456, 0x234567]: 407 // 0x123456: mov $0, 0 408 // 0x12345a: ret 409 // 410 // ... and so on. 411 // 412 // Note that the format string and print arguments are only evaluated when dumpDisassembly 413 // is true, so you can hide expensive disassembly-only computations inside there. 414 415 #define FINALIZE_CODE(linkBufferReference, resultPtrTag, ...) \ 416 FINALIZE_CODE_IF((JSC::Options::asyncDisassembly() || JSC::Options::dumpDisassembly()), linkBufferReference, resultPtrTag, __VA_ARGS__) 417 418 #define FINALIZE_DFG_CODE(linkBufferReference, resultPtrTag, ...) \ 419 FINALIZE_CODE_IF((JSC::Options::asyncDisassembly() || JSC::Options::dumpDisassembly() || Options::dumpDFGDisassembly()), linkBufferReference, resultPtrTag, __VA_ARGS__) 420 421 #define FINALIZE_REGEXP_CODE(linkBufferReference, resultPtrTag, dataLogFArgumentsForHeading) \ 422 FINALIZE_CODE_IF(JSC::Options::asyncDisassembly() || JSC::Options::dumpDisassembly() || Options::dumpRegExpDisassembly(), linkBufferReference, resultPtrTag, dataLogFArgumentsForHeading) 423 424 bool shouldDumpDisassemblyFor(Wasm::CompilationMode); 425 426 #define FINALIZE_WASM_CODE(linkBufferReference, resultPtrTag, ...) \ 427 FINALIZE_CODE_IF((JSC::Options::asyncDisassembly() || JSC::Options::dumpDisassembly() || Options::dumpWasmDisassembly()), linkBufferReference, resultPtrTag, __VA_ARGS__) 428 429 #define FINALIZE_WASM_CODE_FOR_MODE(mode, linkBufferReference, resultPtrTag, ...) \ 430 FINALIZE_CODE_IF(shouldDumpDisassemblyFor(mode), linkBufferReference, resultPtrTag, __VA_ARGS__) 431 432 433 434 } // namespace JSC 435 436 #endif // ENABLE(ASSEMBLER)