• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //
18 // Generated machine code.
19 
20 #ifndef BERBERIS_ASSEMBLER_MACHINE_CODE_H_
21 #define BERBERIS_ASSEMBLER_MACHINE_CODE_H_
22 
23 #include <bit>
24 #include <cstdint>
25 #include <string>
26 
27 #include "berberis/base/arena_alloc.h"
28 #include "berberis/base/arena_vector.h"
29 #include "berberis/base/forever_map.h"
30 #include "berberis/base/macros.h"  // DISALLOW_COPY_AND_ASSIGN
31 
32 #if defined(__riscv)
33 #include <sys/cachectl.h>
34 #endif
35 
36 namespace berberis {
37 
38 enum class InstructionSize {
39   // x86 assembly has 1 byte instructions.
40   OneByte,
41   // riscv and arm64 assembly have 4 bytes instructions.
42   FourBytes,
43 };
44 
45 enum class RelocationType {
46   // Convert absolute address to PC-relative displacement.
47   // Ensure displacement fits in 32-bit value.
48   RelocAbsToDisp32,
49   // Add recovery point and recovery code to global recovery code map.
50   // TODO(eaeltsin): have recovery map for each region instead!
51   RelocRecoveryPoint,
52 };
53 
54 using RecoveryMap = ForeverMap<uintptr_t, uintptr_t>;
55 
56 // Generated machine code for host architecture. Used by trampolines
57 // and JIT translator.
58 // NOTE: this class is not intended for concurrent usage by multiple threads.
59 class MachineCode {
60  public:
MachineCode()61   MachineCode() : code_(&arena_), relocations_(&arena_) {
62     // The amount is chosen according to the performance of spec2000 benchmarks.
63     code_.reserve(1024);
64   }
65 
arena()66   Arena* arena() { return &arena_; }
67 
68   // TODO(eaeltsin): this will include const pool size when supported.
install_size()69   [[nodiscard]] uint32_t install_size() const { return code_.size(); }
70 
code_offset()71   [[nodiscard]] uint32_t code_offset() const { return code_.size(); }
72 
73   template <typename T>
AddrAs(uint32_t offset)74   T* AddrAs(uint32_t offset) {
75     return reinterpret_cast<T*>(AddrOf(offset));
76   }
77 
78   template <typename T>
AddrAs(uint32_t offset)79   [[nodiscard]] const T* AddrAs(uint32_t offset) const {
80     return std::bit_cast<const T*>(AddrOf(offset));
81   }
82 
83   template <typename T>
Add(const T & v)84   void Add(const T& v) {
85     memcpy(AddrAs<T>(Grow(sizeof(T))), &v, sizeof(T));
86   }
87 
88   template <typename T>
AddSequence(const T * v,uint32_t count)89   void AddSequence(const T* v, uint32_t count) {
90     memcpy(AddrAs<T>(Grow(sizeof(T) * count)), v, sizeof(T) * count);
91   }
92 
AddU8(uint8_t v)93   void AddU8(uint8_t v) { code_.push_back(v); }
94 
95   void AsString(std::string* result, InstructionSize insn_size) const;
96 
AddRelocation(uint32_t dst,RelocationType type,uint32_t pc,intptr_t data)97   void AddRelocation(uint32_t dst, RelocationType type, uint32_t pc, intptr_t data) {
98     relocations_.push_back(Relocation{dst, type, pc, data});
99   }
100 
101   // Install to executable memory.
102   template <typename ExecRegionType>
Install(ExecRegionType * exec,const uint8_t * code,RecoveryMap * recovery_map)103   void Install(ExecRegionType* exec, const uint8_t* code, RecoveryMap* recovery_map) {
104     PerformRelocations(code, recovery_map);
105     exec->Write(code, AddrAs<uint8_t>(0), code_.size());
106 #if defined(__riscv)
107     __riscv_flush_icache((void*)code, (void*)(code + code_.size()), 0);
108 #endif
109   }
110 
111   // Install to writable memory.
InstallUnsafe(uint8_t * code,RecoveryMap * recovery_map)112   void InstallUnsafe(uint8_t* code, RecoveryMap* recovery_map) {
113     PerformRelocations(code, recovery_map);
114     memcpy(code, AddrAs<uint8_t>(0), code_.size());
115   }
116 
117   // Print generated code to stderr.
118   void DumpCode(InstructionSize insn_size) const;
119 
120  private:
121   struct Relocation {
122     uint32_t dst;
123     RelocationType type;
124     uint32_t pc;
125     intptr_t data;
126   };
127   using RelocationList = ArenaVector<Relocation>;
128 
129   [[nodiscard]] uint8_t* AddrOf(uint32_t offset);
130   [[nodiscard]] const uint8_t* AddrOf(uint32_t offset) const;
131   uint32_t Grow(uint32_t count);
132 
133   // Relocate the code, in assumption it is to be installed at address 'code'.
134   void PerformRelocations(const uint8_t* code, RecoveryMap* recovery_map);
135 
136   Arena arena_;
137   ArenaVector<uint8_t> code_;
138   RelocationList relocations_;
139 
140   DISALLOW_COPY_AND_ASSIGN(MachineCode);
141 };
142 
143 }  // namespace berberis
144 
145 #endif  // BERBERIS_ASSEMBLER_MACHINE_CODE_H_
146