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