// Copyright 2022 Google LLC // // This source code is licensed under the BSD-style license found in the // LICENSE file in the root directory of this source tree. #pragma once #include #include #include typedef uint8_t byte; namespace xnnpack { constexpr size_t kInstructionSizeInBytes = 4; constexpr size_t kInstructionSizeInBytesLog2 = 2; enum class Error { kNoError, kOutOfMemory, kInvalidOperand, kLabelAlreadyBound, kLabelOffsetOutOfBounds, kLabelHasTooManyUsers, kInvalidLaneIndex, kInvalidRegisterListLength, kFinalizeCodeMemoryFail, kUnimplemented, }; constexpr size_t max_label_users = 10; // Label is a target of a branch. You call Assembler::bind to bind a label to an // actual location in the instruction stream. // // ``` // Label l; // b(kAl, l1); // branch to an unbound label is fine, it will be patched later. // a.bind(l); // binds label to this location in the instruction stream. // b(kAl, l1); // branch to an already bound label. // ``` struct Label { // Location of label within Assembler buffer. byte* offset = nullptr; // A label can only be bound once, binding it again leads to an error. bool bound = (offset != nullptr); // All users of this label, recorded by their offset in the Assembler buffer. std::array users{{0}}; size_t num_users = 0; // Records a user (e.g. branch instruction) of this label. // Returns true if success, false if number of users exceeds maximum. bool add_use(byte* offset) { if (num_users >= max_label_users) { return false; } users[num_users++] = offset; return true; } }; class AssemblerBase { public: // Takes an xnn_code_buffer with a pointer to allocated memory. If the buffer // already contains content (size != 0), appends to after size (up to capacity). explicit AssemblerBase(xnn_code_buffer* buf); // Write value into the code buffer and advances cursor_. void emit32(uint32_t value); // Finish assembly of code, this should be the last function called on an // instance of Assembler. Returns a pointer to the start of code region. void* finalize(); // Reset the assembler state (no memory is freed). void reset(); // Get a pointer to the start of code buffer. const byte* start() const { return buffer_; } const byte* offset() const { return cursor_; } template const T offset() const { return reinterpret_cast(cursor_); } // Returns the number of bytes of code actually in the buffer. size_t code_size_in_bytes() const { return (cursor_ - buffer_); } const Error error() const { return error_; } protected: // Pointer into code buffer to start writing code. byte* buffer_; // Pointer to current position in code buffer. byte* cursor_; // Pointer to out-of-bounds of code buffer. byte* top_; // Errors encountered while assembling code. Error error_ = Error::kNoError; // Holds an xnn_code_buffer, will write code to its code pointer, and unmap // unused pages on finalizing. xnn_code_buffer* xnn_buffer = nullptr; }; } // namespace xnnpack