Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2008-2017, Petr Kobalicek 3 : 4 : This software is provided 'as-is', without any express or implied 5 : warranty. In no event will the authors be held liable for any damages 6 : arising from the use of this software. 7 : 8 : Permission is granted to anyone to use this software for any purpose, 9 : including commercial applications, and to alter it and redistribute it 10 : freely, subject to the following restrictions: 11 : 12 : 1. The origin of this software must not be misrepresented; you must not 13 : claim that you wrote the original software. If you use this software 14 : in a product, an acknowledgment in the product documentation would be 15 : appreciated but is not required. 16 : 2. Altered source versions must be plainly marked as such, and must not be 17 : misrepresented as being the original software. 18 : 3. This notice may not be removed or altered from any source distribution. 19 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 20 : #ifdef __PLUMED_HAS_ASMJIT 21 : #pragma GCC diagnostic push 22 : #pragma GCC diagnostic ignored "-Wpedantic" 23 : // [AsmJit] 24 : // Complete x86/x64 JIT and Remote Assembler for C++. 25 : // 26 : // [License] 27 : // Zlib - See LICENSE.md file in the package. 28 : 29 : // [Export] 30 : #define ASMJIT_EXPORTS 31 : 32 : // [Guard] 33 : #include "./asmjit_build.h" 34 : #if !defined(ASMJIT_DISABLE_COMPILER) 35 : 36 : // [Dependencies] 37 : #include "./regalloc_p.h" 38 : #include "./utils.h" 39 : 40 : // [Api-Begin] 41 : #include "./asmjit_apibegin.h" 42 : 43 : namespace PLMD { 44 : namespace asmjit { 45 : 46 : // ============================================================================ 47 : // [asmjit::RAPass - Construction / Destruction] 48 : // ============================================================================ 49 : 50 1944 : RAPass::RAPass() noexcept : 51 : CBPass("RA"), 52 1944 : _varMapToVaListOffset(0) {} 53 0 : RAPass::~RAPass() noexcept {} 54 : 55 : // ============================================================================ 56 : // [asmjit::RAPass - Interface] 57 : // ============================================================================ 58 : 59 1944 : Error RAPass::process(Zone* zone) noexcept { 60 1944 : _zone = zone; 61 1944 : _heap.reset(zone); 62 1944 : _emitComments = (cb()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) != 0; 63 : 64 : Error err = kErrorOk; 65 : CBNode* node = cc()->getFirstNode(); 66 1944 : if (!node) return err; 67 : 68 : do { 69 1944 : if (node->getType() == CBNode::kNodeFunc) { 70 : CCFunc* func = static_cast<CCFunc*>(node); 71 : node = func->getEnd(); 72 : 73 1944 : err = compile(func); 74 1944 : if (err) break; 75 : } 76 : 77 : // Find a function by skipping all nodes that are not `kNodeFunc`. 78 : do { 79 : node = node->getNext(); 80 1944 : } while (node && node->getType() != CBNode::kNodeFunc); 81 1944 : } while (node); 82 : 83 1944 : _heap.reset(nullptr); 84 1944 : _zone = nullptr; 85 1944 : return err; 86 : } 87 : 88 1944 : Error RAPass::compile(CCFunc* func) noexcept { 89 1944 : ASMJIT_PROPAGATE(prepare(func)); 90 : 91 : Error err; 92 : do { 93 1944 : err = fetch(); 94 1944 : if (err) break; 95 : 96 1944 : err = removeUnreachableCode(); 97 1944 : if (err) break; 98 : 99 1944 : err = livenessAnalysis(); 100 1944 : if (err) break; 101 : 102 : #if !defined(ASMJIT_DISABLE_LOGGING) 103 1944 : if (cc()->getGlobalOptions() & CodeEmitter::kOptionLoggingEnabled) { 104 0 : err = annotate(); 105 0 : if (err) break; 106 : } 107 : #endif // !ASMJIT_DISABLE_LOGGING 108 : 109 1944 : err = translate(); 110 : } while (false); 111 : 112 1944 : cleanup(); 113 : 114 : // We alter the compiler cursor, because it doesn't make sense to reference 115 : // it after compilation - some nodes may disappear and it's forbidden to add 116 : // new code after the compilation is done. 117 : cc()->_setCursor(nullptr); 118 1944 : return err; 119 : } 120 : 121 1944 : Error RAPass::prepare(CCFunc* func) noexcept { 122 : CBNode* end = func->getEnd(); 123 : 124 1944 : _func = func; 125 1944 : _stop = end->getNext(); 126 : 127 : _unreachableList.reset(); 128 : _returningList.reset(); 129 : _jccList.reset(); 130 : _contextVd.reset(); 131 : 132 1944 : _memVarCells = nullptr; 133 1944 : _memStackCells = nullptr; 134 : 135 1944 : _mem1ByteVarsUsed = 0; 136 1944 : _mem2ByteVarsUsed = 0; 137 1944 : _mem4ByteVarsUsed = 0; 138 1944 : _mem8ByteVarsUsed = 0; 139 1944 : _mem16ByteVarsUsed = 0; 140 1944 : _mem32ByteVarsUsed = 0; 141 1944 : _mem64ByteVarsUsed = 0; 142 1944 : _memStackCellsUsed = 0; 143 : 144 1944 : _memMaxAlign = 0; 145 1944 : _memVarTotal = 0; 146 1944 : _memStackTotal = 0; 147 1944 : _memAllTotal = 0; 148 1944 : _annotationLength = 12; 149 : 150 1944 : return kErrorOk; 151 : } 152 : 153 1944 : void RAPass::cleanup() noexcept { 154 : VirtReg** virtArray = _contextVd.getData(); 155 : size_t virtCount = _contextVd.getLength(); 156 : 157 25684 : for (size_t i = 0; i < virtCount; i++) { 158 23740 : VirtReg* vreg = virtArray[i]; 159 23740 : vreg->_raId = kInvalidValue; 160 : vreg->resetPhysId(); 161 : } 162 : 163 : _contextVd.reset(); 164 1944 : } 165 : 166 : // ============================================================================ 167 : // [asmjit::RAPass - Mem] 168 : // ============================================================================ 169 : 170 : static ASMJIT_INLINE uint32_t RAGetDefaultAlignment(uint32_t size) { 171 0 : if (size > 32) 172 : return 64; 173 0 : else if (size > 16) 174 : return 32; 175 0 : else if (size > 8) 176 : return 16; 177 0 : else if (size > 4) 178 : return 8; 179 0 : else if (size > 2) 180 : return 4; 181 0 : else if (size > 1) 182 : return 2; 183 : else 184 0 : return 1; 185 : } 186 : 187 4004 : RACell* RAPass::_newVarCell(VirtReg* vreg) { 188 : ASMJIT_ASSERT(vreg->_memCell == nullptr); 189 : 190 : RACell* cell; 191 4004 : uint32_t size = vreg->getSize(); 192 : 193 4004 : if (vreg->isStack()) { 194 0 : cell = _newStackCell(size, vreg->getAlignment()); 195 0 : if (ASMJIT_UNLIKELY(!cell)) return nullptr; 196 : } 197 : else { 198 4004 : cell = static_cast<RACell*>(_zone->alloc(sizeof(RACell))); 199 4004 : if (!cell) goto _NoMemory; 200 : 201 4004 : cell->next = _memVarCells; 202 4004 : cell->offset = 0; 203 4004 : cell->size = size; 204 4004 : cell->alignment = size; 205 : 206 4004 : _memVarCells = cell; 207 4004 : _memMaxAlign = std::max<uint32_t>(_memMaxAlign, size); 208 4004 : _memVarTotal += size; 209 : 210 4004 : switch (size) { 211 0 : case 1: _mem1ByteVarsUsed++ ; break; 212 0 : case 2: _mem2ByteVarsUsed++ ; break; 213 0 : case 4: _mem4ByteVarsUsed++ ; break; 214 4004 : case 8: _mem8ByteVarsUsed++ ; break; 215 0 : case 16: _mem16ByteVarsUsed++; break; 216 0 : case 32: _mem32ByteVarsUsed++; break; 217 0 : case 64: _mem64ByteVarsUsed++; break; 218 : 219 0 : default: 220 0 : ASMJIT_NOT_REACHED(); 221 : } 222 : } 223 : 224 4004 : vreg->_memCell = cell; 225 4004 : return cell; 226 : 227 : _NoMemory: 228 0 : cc()->setLastError(DebugUtils::errored(kErrorNoHeapMemory)); 229 : return nullptr; 230 : } 231 : 232 0 : RACell* RAPass::_newStackCell(uint32_t size, uint32_t alignment) { 233 0 : RACell* cell = static_cast<RACell*>(_zone->alloc(sizeof(RACell))); 234 0 : if (ASMJIT_UNLIKELY(!cell)) return nullptr; 235 : 236 0 : if (alignment == 0) 237 0 : alignment = RAGetDefaultAlignment(size); 238 : 239 0 : if (alignment > 64) 240 0 : alignment = 64; 241 : 242 : ASMJIT_ASSERT(Utils::isPowerOf2(alignment)); 243 0 : size = Utils::alignTo<uint32_t>(size, alignment); 244 : 245 : // Insert it sorted according to the alignment and size. 246 : { 247 0 : RACell** pPrev = &_memStackCells; 248 0 : RACell* cur = *pPrev; 249 : 250 0 : while (cur && ((cur->alignment > alignment) || (cur->alignment == alignment && cur->size > size))) { 251 0 : pPrev = &cur->next; 252 0 : cur = *pPrev; 253 : } 254 : 255 0 : cell->next = cur; 256 0 : cell->offset = 0; 257 0 : cell->size = size; 258 0 : cell->alignment = alignment; 259 : 260 0 : *pPrev = cell; 261 0 : _memStackCellsUsed++; 262 : 263 0 : _memMaxAlign = std::max<uint32_t>(_memMaxAlign, alignment); 264 0 : _memStackTotal += size; 265 : } 266 : 267 0 : return cell; 268 : } 269 : 270 1944 : Error RAPass::resolveCellOffsets() { 271 1944 : RACell* varCell = _memVarCells; 272 1944 : RACell* stackCell = _memStackCells; 273 : 274 : uint32_t pos64 = 0; 275 1944 : uint32_t pos32 = pos64 + _mem64ByteVarsUsed * 64; 276 1944 : uint32_t pos16 = pos32 + _mem32ByteVarsUsed * 32; 277 1944 : uint32_t pos8 = pos16 + _mem16ByteVarsUsed * 16; 278 1944 : uint32_t pos4 = pos8 + _mem8ByteVarsUsed * 8 ; 279 1944 : uint32_t pos2 = pos4 + _mem4ByteVarsUsed * 4 ; 280 1944 : uint32_t pos1 = pos2 + _mem2ByteVarsUsed * 2 ; 281 : 282 : // Assign home slots. 283 5948 : while (varCell) { 284 4004 : uint32_t size = varCell->size; 285 : uint32_t offset = 0; 286 : 287 4004 : switch (size) { 288 0 : case 1: offset = pos1 ; pos1 += 1 ; break; 289 0 : case 2: offset = pos2 ; pos2 += 2 ; break; 290 0 : case 4: offset = pos4 ; pos4 += 4 ; break; 291 4004 : case 8: offset = pos8 ; pos8 += 8 ; break; 292 0 : case 16: offset = pos16; pos16 += 16; break; 293 0 : case 32: offset = pos32; pos32 += 32; break; 294 0 : case 64: offset = pos64; pos64 += 64; break; 295 : 296 0 : default: 297 0 : ASMJIT_NOT_REACHED(); 298 : } 299 : 300 4004 : varCell->offset = static_cast<int32_t>(offset); 301 4004 : varCell = varCell->next; 302 : } 303 : 304 : // Assign stack slots. 305 1944 : uint32_t stackPos = pos1 + _mem1ByteVarsUsed; 306 1944 : while (stackCell) { 307 0 : uint32_t size = stackCell->size; 308 0 : uint32_t alignment = stackCell->alignment; 309 : ASMJIT_ASSERT(alignment != 0 && Utils::isPowerOf2(alignment)); 310 : 311 : stackPos = Utils::alignTo(stackPos, alignment); 312 0 : stackCell->offset = stackPos; 313 0 : stackCell = stackCell->next; 314 : 315 0 : stackPos += size; 316 : } 317 : 318 1944 : _memAllTotal = stackPos; 319 1944 : return kErrorOk; 320 : } 321 : 322 : // ============================================================================ 323 : // [asmjit::RAPass - RemoveUnreachableCode] 324 : // ============================================================================ 325 : 326 1944 : Error RAPass::removeUnreachableCode() { 327 : ZoneList<CBNode*>::Link* link = _unreachableList.getFirst(); 328 : CBNode* stop = getStop(); 329 : 330 3888 : while (link) { 331 : CBNode* node = link->getValue(); 332 1944 : if (node && node->getPrev() && node != stop) { 333 : // Locate all unreachable nodes. 334 : CBNode* first = node; 335 : do { 336 1944 : if (node->hasPassData()) break; 337 : node = node->getNext(); 338 0 : } while (node != stop); 339 : 340 : // Remove unreachable nodes that are neither informative nor directives. 341 1944 : if (node != first) { 342 : CBNode* end = node; 343 : node = first; 344 : 345 : // NOTE: The strategy is as follows: 346 : // 1. The algorithm removes everything until it finds a first label. 347 : // 2. After the first label is found it removes only removable nodes. 348 : bool removeEverything = true; 349 : do { 350 : CBNode* next = node->getNext(); 351 : bool remove = node->isRemovable(); 352 : 353 0 : if (!remove) { 354 0 : if (node->isLabel()) 355 : removeEverything = false; 356 : remove = removeEverything; 357 : } 358 : 359 0 : if (remove) 360 0 : cc()->removeNode(node); 361 : 362 : node = next; 363 0 : } while (node != end); 364 : } 365 : } 366 : 367 : link = link->getNext(); 368 : } 369 : 370 1944 : return kErrorOk; 371 : } 372 : 373 : // ============================================================================ 374 : // [asmjit::RAPass - Liveness Analysis] 375 : // ============================================================================ 376 : 377 : //! \internal 378 : struct LivenessTarget { 379 : LivenessTarget* prev; //!< Previous target. 380 : CBLabel* node; //!< Target node. 381 : CBJump* from; //!< Jumped from. 382 : }; 383 : 384 1944 : Error RAPass::livenessAnalysis() { 385 : uint32_t bLen = static_cast<uint32_t>( 386 1944 : ((_contextVd.getLength() + RABits::kEntityBits - 1) / RABits::kEntityBits)); 387 : 388 : // No variables. 389 1944 : if (bLen == 0) 390 : return kErrorOk; 391 : 392 : CCFunc* func = getFunc(); 393 : CBJump* from = nullptr; 394 : 395 : LivenessTarget* ltCur = nullptr; 396 : LivenessTarget* ltUnused = nullptr; 397 : 398 : ZoneList<CBNode*>::Link* retPtr = _returningList.getFirst(); 399 : ASMJIT_ASSERT(retPtr != nullptr); 400 : 401 : CBNode* node = retPtr->getValue(); 402 : RAData* wd; 403 : 404 1944 : size_t varMapToVaListOffset = _varMapToVaListOffset; 405 : RABits* bCur = newBits(bLen); 406 1944 : if (ASMJIT_UNLIKELY(!bCur)) goto NoMem; 407 : 408 : // Allocate bits for code visited first time. 409 36630 : Visit: 410 : for (;;) { 411 : wd = node->getPassData<RAData>(); 412 36630 : if (wd->liveness) { 413 0 : if (bCur->_addBitsDelSource(wd->liveness, bCur, bLen)) 414 0 : goto Patch; 415 : else 416 0 : goto Done; 417 : } 418 : 419 : RABits* bTmp = copyBits(bCur, bLen); 420 36630 : if (!bTmp) goto NoMem; 421 : 422 : wd = node->getPassData<RAData>(); 423 36630 : wd->liveness = bTmp; 424 : 425 36630 : uint32_t tiedTotal = wd->tiedTotal; 426 : TiedReg* tiedArray = reinterpret_cast<TiedReg*>(((uint8_t*)wd) + varMapToVaListOffset); 427 : 428 98004 : for (uint32_t i = 0; i < tiedTotal; i++) { 429 61374 : TiedReg* tied = &tiedArray[i]; 430 61374 : VirtReg* vreg = tied->vreg; 431 : 432 61374 : uint32_t flags = tied->flags; 433 61374 : uint32_t raId = vreg->_raId; 434 : 435 61374 : if ((flags & TiedReg::kWAll) && !(flags & TiedReg::kRAll)) { 436 : // Write-Only. 437 : bTmp->setBit(raId); 438 : bCur->delBit(raId); 439 : } 440 : else { 441 : // Read-Only or Read/Write. 442 : bTmp->setBit(raId); 443 : bCur->setBit(raId); 444 : } 445 : } 446 : 447 36630 : if (node->getType() == CBNode::kNodeLabel) 448 0 : goto Target; 449 : 450 36630 : if (node == func) 451 1944 : goto Done; 452 : 453 : ASMJIT_ASSERT(node->getPrev()); 454 : node = node->getPrev(); 455 34686 : } 456 : 457 : // Patch already generated liveness bits. 458 0 : Patch: 459 : for (;;) { 460 : ASMJIT_ASSERT(node->hasPassData()); 461 : ASMJIT_ASSERT(node->getPassData<RAData>()->liveness != nullptr); 462 : 463 0 : RABits* bNode = node->getPassData<RAData>()->liveness; 464 0 : if (!bNode->_addBitsDelSource(bCur, bLen)) goto Done; 465 0 : if (node->getType() == CBNode::kNodeLabel) goto Target; 466 : 467 0 : if (node == func) goto Done; 468 : node = node->getPrev(); 469 0 : } 470 : 471 0 : Target: 472 0 : if (static_cast<CBLabel*>(node)->getNumRefs() != 0) { 473 : // Push a new LivenessTarget onto the stack if needed. 474 0 : if (!ltCur || ltCur->node != node) { 475 : // Allocate a new LivenessTarget object (from pool or zone). 476 : LivenessTarget* ltTmp = ltUnused; 477 : 478 0 : if (ltTmp) { 479 0 : ltUnused = ltUnused->prev; 480 : } 481 : else { 482 0 : ltTmp = _zone->allocT<LivenessTarget>( 483 0 : sizeof(LivenessTarget) - sizeof(RABits) + bLen * sizeof(uintptr_t)); 484 0 : if (!ltTmp) goto NoMem; 485 : } 486 : 487 : // Initialize and make current - ltTmp->from will be set later on. 488 0 : ltTmp->prev = ltCur; 489 0 : ltTmp->node = static_cast<CBLabel*>(node); 490 : ltCur = ltTmp; 491 : 492 : from = static_cast<CBLabel*>(node)->getFrom(); 493 : ASMJIT_ASSERT(from != nullptr); 494 0 : } 495 : else { 496 0 : from = ltCur->from; 497 0 : goto JumpNext; 498 : } 499 : 500 : // Visit/Patch. 501 : do { 502 0 : ltCur->from = from; 503 0 : bCur->copyBits(node->getPassData<RAData>()->liveness, bLen); 504 : 505 0 : if (!from->getPassData<RAData>()->liveness) { 506 : node = from; 507 0 : goto Visit; 508 : } 509 : 510 : // Issue #25: Moved 'JumpNext' here since it's important to patch 511 : // code again if there are more live variables than before. 512 0 : JumpNext: 513 0 : if (bCur->delBits(from->getPassData<RAData>()->liveness, bLen)) { 514 : node = from; 515 0 : goto Patch; 516 : } 517 : 518 : from = from->getJumpNext(); 519 0 : } while (from); 520 : 521 : // Pop the current LivenessTarget from the stack. 522 : { 523 : LivenessTarget* ltTmp = ltCur; 524 0 : ltCur = ltCur->prev; 525 0 : ltTmp->prev = ltUnused; 526 : ltUnused = ltTmp; 527 : } 528 : } 529 : 530 0 : bCur->copyBits(node->getPassData<RAData>()->liveness, bLen); 531 : node = node->getPrev(); 532 0 : if (node->isJmp() || !node->hasPassData()) goto Done; 533 : 534 : wd = node->getPassData<RAData>(); 535 0 : if (!wd->liveness) goto Visit; 536 0 : if (bCur->delBits(wd->liveness, bLen)) goto Patch; 537 : 538 0 : Done: 539 1944 : if (ltCur) { 540 0 : node = ltCur->node; 541 0 : from = ltCur->from; 542 : 543 0 : goto JumpNext; 544 : } 545 : 546 : retPtr = retPtr->getNext(); 547 1944 : if (retPtr) { 548 : node = retPtr->getValue(); 549 0 : goto Visit; 550 : } 551 : 552 : return kErrorOk; 553 : 554 : NoMem: 555 : return DebugUtils::errored(kErrorNoHeapMemory); 556 : } 557 : 558 : // ============================================================================ 559 : // [asmjit::RAPass - Annotate] 560 : // ============================================================================ 561 : 562 0 : Error RAPass::formatInlineComment(StringBuilder& dst, CBNode* node) { 563 : #if !defined(ASMJIT_DISABLE_LOGGING) 564 : RAData* wd = node->getPassData<RAData>(); 565 : 566 0 : if (node->hasInlineComment()) 567 : dst.appendString(node->getInlineComment()); 568 : 569 0 : if (wd && wd->liveness) { 570 0 : if (dst.getLength() < _annotationLength) 571 0 : dst.appendChars(' ', _annotationLength - dst.getLength()); 572 : 573 0 : uint32_t vdCount = static_cast<uint32_t>(_contextVd.getLength()); 574 0 : size_t offset = dst.getLength() + 1; 575 : 576 : dst.appendChar('['); 577 : dst.appendChars(' ', vdCount); 578 : dst.appendChar(']'); 579 0 : RABits* liveness = wd->liveness; 580 : 581 : uint32_t i; 582 0 : for (i = 0; i < vdCount; i++) { 583 0 : if (liveness->getBit(i)) 584 0 : dst.getData()[offset + i] = '.'; 585 : } 586 : 587 0 : uint32_t tiedTotal = wd->tiedTotal; 588 0 : TiedReg* tiedArray = reinterpret_cast<TiedReg*>(((uint8_t*)wd) + _varMapToVaListOffset); 589 : 590 0 : for (i = 0; i < tiedTotal; i++) { 591 0 : TiedReg* tied = &tiedArray[i]; 592 0 : VirtReg* vreg = tied->vreg; 593 0 : uint32_t flags = tied->flags; 594 : 595 : char c = 'u'; 596 0 : if ( (flags & TiedReg::kRAll) && !(flags & TiedReg::kWAll)) c = 'r'; 597 0 : if (!(flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'w'; 598 0 : if ( (flags & TiedReg::kRAll) && (flags & TiedReg::kWAll)) c = 'x'; 599 : // Uppercase if unused. 600 0 : if ( (flags & TiedReg::kUnuse)) c -= 'a' - 'A'; 601 : 602 : ASMJIT_ASSERT(offset + vreg->_raId < dst.getLength()); 603 0 : dst._data[offset + vreg->_raId] = c; 604 : } 605 : } 606 : #endif // !ASMJIT_DISABLE_LOGGING 607 : 608 0 : return kErrorOk; 609 : } 610 : 611 : } // asmjit namespace 612 : } // namespace PLMD 613 : 614 : // [Api-End] 615 : #include "./asmjit_apiend.h" 616 : 617 : // [Guard] 618 : #endif // !ASMJIT_DISABLE_COMPILER 619 : #pragma GCC diagnostic pop 620 : #endif // __PLUMED_HAS_ASMJIT