1 // Copyright 2022 Google LLC 2 // 3 // This source code is licensed under the BSD-style license found in the 4 // LICENSE file in the root directory of this source tree. 5 6 #pragma once 7 8 #include <xnnpack/allocator.h> 9 10 #include <array> 11 #include <cstdint> 12 13 typedef uint8_t byte; 14 15 namespace xnnpack { 16 17 constexpr size_t kInstructionSizeInBytes = 4; 18 constexpr size_t kInstructionSizeInBytesLog2 = 2; 19 20 enum class Error { 21 kNoError, 22 kOutOfMemory, 23 kInvalidOperand, 24 kLabelAlreadyBound, 25 kLabelOffsetOutOfBounds, 26 kLabelHasTooManyUsers, 27 kInvalidLaneIndex, 28 kInvalidRegisterListLength, 29 kFinalizeCodeMemoryFail, 30 kUnimplemented, 31 }; 32 33 constexpr size_t max_label_users = 10; 34 // Label is a target of a branch. You call Assembler::bind to bind a label to an 35 // actual location in the instruction stream. 36 // 37 // ``` 38 // Label l; 39 // b(kAl, l1); // branch to an unbound label is fine, it will be patched later. 40 // a.bind(l); // binds label to this location in the instruction stream. 41 // b(kAl, l1); // branch to an already bound label. 42 // ``` 43 struct Label { 44 // Location of label within Assembler buffer. 45 byte* offset = nullptr; 46 // A label can only be bound once, binding it again leads to an error. 47 bool bound = (offset != nullptr); 48 // All users of this label, recorded by their offset in the Assembler buffer. 49 std::array<byte*, max_label_users> users{{0}}; 50 size_t num_users = 0; 51 52 // Records a user (e.g. branch instruction) of this label. 53 // Returns true if success, false if number of users exceeds maximum. add_useLabel54 bool add_use(byte* offset) { 55 if (num_users >= max_label_users) { 56 return false; 57 } 58 users[num_users++] = offset; 59 return true; 60 } 61 }; 62 63 class AssemblerBase { 64 public: 65 // Takes an xnn_code_buffer with a pointer to allocated memory. If the buffer 66 // already contains content (size != 0), appends to after size (up to capacity). 67 explicit AssemblerBase(xnn_code_buffer* buf); 68 69 // Write value into the code buffer and advances cursor_. 70 void emit32(uint32_t value); 71 // Finish assembly of code, this should be the last function called on an 72 // instance of Assembler. Returns a pointer to the start of code region. 73 void* finalize(); 74 // Reset the assembler state (no memory is freed). 75 void reset(); 76 77 // Get a pointer to the start of code buffer. start()78 const byte* start() const { return buffer_; } offset()79 const byte* offset() const { return cursor_; } 80 template<typename T> offset()81 const T offset() const { return reinterpret_cast<T>(cursor_); } 82 // Returns the number of bytes of code actually in the buffer. code_size_in_bytes()83 size_t code_size_in_bytes() const { return (cursor_ - buffer_); } error()84 const Error error() const { return error_; } 85 86 protected: 87 // Pointer into code buffer to start writing code. 88 byte* buffer_; 89 // Pointer to current position in code buffer. 90 byte* cursor_; 91 // Pointer to out-of-bounds of code buffer. 92 byte* top_; 93 // Errors encountered while assembling code. 94 Error error_ = Error::kNoError; 95 // Holds an xnn_code_buffer, will write code to its code pointer, and unmap 96 // unused pages on finalizing. 97 xnn_code_buffer* xnn_buffer = nullptr; 98 }; 99 100 } // namespace xnnpack 101