|  | //===- subzero/src/IceAssembler.cpp - Assembler base class ----------------===// | 
|  | // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  | // | 
|  | // Modified by the Subzero authors. | 
|  | // | 
|  | // This is forked from Dart revision 39313. | 
|  | // Please update the revision if we merge back changes from Dart. | 
|  | // https://code.google.com/p/dart/wiki/GettingTheSource | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | //                        The Subzero Code Generator | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | /// | 
|  | /// \file | 
|  | /// \brief Implements the Assembler base class. | 
|  | /// | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "IceAssembler.h" | 
|  |  | 
|  | #include "IceGlobalContext.h" | 
|  | #include "IceOperand.h" | 
|  |  | 
|  | namespace Ice { | 
|  |  | 
|  | static uintptr_t NewContents(Assembler &Assemblr, intptr_t Capacity) { | 
|  | uintptr_t Result = Assemblr.allocateBytes(Capacity); | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | void Label::linkTo(const Assembler &Asm, intptr_t Pos) { | 
|  | // We must not set the link until the position is absolutely known. This means | 
|  | // not during the preliminary (sandboxing) pass, and not when the instruction | 
|  | // needs a text fixup (hybrid iasm mode). | 
|  | if (Asm.getPreliminary() || Asm.needsTextFixup()) | 
|  | return; | 
|  | assert(!isBound()); | 
|  | Position = Pos + kWordSize; | 
|  | assert(isLinked()); | 
|  | } | 
|  |  | 
|  | void AssemblerBuffer::installFixup(AssemblerFixup *F) { | 
|  | if (!Assemblr.getPreliminary()) | 
|  | Fixups.push_back(F); | 
|  | } | 
|  |  | 
|  | AssemblerFixup *AssemblerBuffer::createFixup(FixupKind Kind, | 
|  | const Constant *Value) { | 
|  | AssemblerFixup *F = | 
|  | new (Assemblr.allocate<AssemblerFixup>()) AssemblerFixup(); | 
|  | F->set_kind(Kind); | 
|  | F->set_value(Value); | 
|  | installFixup(F); | 
|  | return F; | 
|  | } | 
|  |  | 
|  | AssemblerTextFixup *AssemblerBuffer::createTextFixup(const std::string &Text, | 
|  | size_t BytesUsed) { | 
|  | AssemblerTextFixup *F = new (Assemblr.allocate<AssemblerTextFixup>()) | 
|  | AssemblerTextFixup(Text, BytesUsed); | 
|  | installFixup(F); | 
|  | resetNeedsTextFixup(); | 
|  | return F; | 
|  | } | 
|  |  | 
|  | void AssemblerBuffer::EnsureCapacity::validate(AssemblerBuffer *buffer) { | 
|  | // In debug mode, we save the assembler buffer along with the gap size before | 
|  | // we start emitting to the buffer. This allows us to check that any single | 
|  | // generated instruction doesn't overflow the limit implied by the minimum | 
|  | // gap size. | 
|  | Gap = computeGap(); | 
|  | // Make sure that extending the capacity leaves a big enough gap for any kind | 
|  | // of instruction. | 
|  | assert(Gap >= kMinimumGap); | 
|  | // Mark the buffer as having ensured the capacity. | 
|  | assert(!buffer->hasEnsuredCapacity()); // Cannot nest. | 
|  | buffer->HasEnsuredCapacity = true; | 
|  | } | 
|  |  | 
|  | AssemblerBuffer::EnsureCapacity::~EnsureCapacity() { | 
|  | // Unmark the buffer, so we cannot emit after this. | 
|  | Buffer->HasEnsuredCapacity = false; | 
|  | // Make sure the generated instruction doesn't take up more space than the | 
|  | // minimum gap. | 
|  | intptr_t delta = Gap - computeGap(); | 
|  | (void)delta; | 
|  | assert(delta <= kMinimumGap); | 
|  | } | 
|  |  | 
|  | AssemblerBuffer::AssemblerBuffer(Assembler &Asm) : Assemblr(Asm) { | 
|  | constexpr intptr_t OneKB = 1024; | 
|  | static constexpr intptr_t kInitialBufferCapacity = 4 * OneKB; | 
|  | Contents = NewContents(Assemblr, kInitialBufferCapacity); | 
|  | Cursor = Contents; | 
|  | Limit = computeLimit(Contents, kInitialBufferCapacity); | 
|  | HasEnsuredCapacity = false; | 
|  | TextFixupNeeded = false; | 
|  |  | 
|  | // Verify internal state. | 
|  | assert(capacity() == kInitialBufferCapacity); | 
|  | assert(size() == 0); | 
|  | } | 
|  |  | 
|  | AssemblerBuffer::~AssemblerBuffer() = default; | 
|  |  | 
|  | void AssemblerBuffer::extendCapacity() { | 
|  | intptr_t old_size = size(); | 
|  | intptr_t old_capacity = capacity(); | 
|  | constexpr intptr_t OneMB = 1 << 20; | 
|  | intptr_t new_capacity = std::min(old_capacity * 2, old_capacity + OneMB); | 
|  | if (new_capacity < old_capacity) { | 
|  | llvm::report_fatal_error( | 
|  | "Unexpected overflow in AssemblerBuffer::ExtendCapacity"); | 
|  | } | 
|  |  | 
|  | // Allocate the new data area and copy contents of the old one to it. | 
|  | uintptr_t new_contents = NewContents(Assemblr, new_capacity); | 
|  | memmove(reinterpret_cast<void *>(new_contents), | 
|  | reinterpret_cast<void *>(Contents), old_size); | 
|  |  | 
|  | // Compute the relocation delta and switch to the new contents area. | 
|  | intptr_t delta = new_contents - Contents; | 
|  | Contents = new_contents; | 
|  |  | 
|  | // Update the cursor and recompute the limit. | 
|  | Cursor += delta; | 
|  | Limit = computeLimit(new_contents, new_capacity); | 
|  |  | 
|  | // Verify internal state. | 
|  | assert(capacity() == new_capacity); | 
|  | assert(size() == old_size); | 
|  | } | 
|  |  | 
|  | llvm::StringRef Assembler::getBufferView() const { | 
|  | return llvm::StringRef(reinterpret_cast<const char *>(Buffer.contents()), | 
|  | Buffer.size()); | 
|  | } | 
|  |  | 
|  | void Assembler::bindRelocOffset(RelocOffset *Offset) { | 
|  | if (!getPreliminary()) { | 
|  | Offset->setOffset(Buffer.getPosition()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Assembler::emitIASBytes(GlobalContext *Ctx) const { | 
|  | Ostream &Str = Ctx->getStrEmit(); | 
|  | intptr_t EndPosition = Buffer.size(); | 
|  | intptr_t CurPosition = 0; | 
|  | for (const AssemblerFixup *NextFixup : fixups()) { | 
|  | intptr_t NextFixupLoc = NextFixup->position(); | 
|  | for (intptr_t i = CurPosition; i < NextFixupLoc; ++i) { | 
|  | Str << "\t.byte 0x"; | 
|  | Str.write_hex(Buffer.load<uint8_t>(i)); | 
|  | Str << "\n"; | 
|  | } | 
|  | CurPosition = NextFixupLoc + NextFixup->emit(Ctx, *this); | 
|  | assert(CurPosition <= EndPosition); | 
|  | } | 
|  | // Handle any bytes that are not prefixed by a fixup. | 
|  | for (intptr_t i = CurPosition; i < EndPosition; ++i) { | 
|  | Str << "\t.byte 0x"; | 
|  | Str.write_hex(Buffer.load<uint8_t>(i)); | 
|  | Str << "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | } // end of namespace Ice |