1 /*
2  * Copyright (C) 2023 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 #ifndef BERBERIS_RUNTIME_PRIMITIVES_CODE_POOL_H_
18 #define BERBERIS_RUNTIME_PRIMITIVES_CODE_POOL_H_
19 
20 #include <cstdint>
21 #include <mutex>
22 
23 #include "berberis/assembler/machine_code.h"
24 #include "berberis/base/arena_alloc.h"
25 #include "berberis/base/exec_region.h"
26 #include "berberis/runtime_primitives/exec_region_anonymous.h"
27 #include "berberis/runtime_primitives/host_code.h"
28 
29 #if defined(__BIONIC__)
30 #include "berberis/runtime_primitives/exec_region_elf_backed.h"
31 #endif
32 
33 namespace berberis {
34 
35 // Code pool is an arena used to store fragments of generated code.
36 // TODO(b/232598137): Consider freeing allocated regions.
37 template <typename ExecRegionFactory>
38 class CodePool {
39  public:
40   CodePool() = default;
41 
42   // Not copyable or movable
43   CodePool(const CodePool&) = delete;
44   CodePool& operator=(const CodePool&) = delete;
45   CodePool(CodePool&&) = delete;
46   CodePool& operator=(CodePool&&) = delete;
47 
Add(MachineCode * code)48   [[nodiscard]] HostCodeAddr Add(MachineCode* code) {
49     std::lock_guard<std::mutex> lock(mutex_);
50 
51     uint32_t size = code->install_size();
52 
53     // Align region start on 64-byte cache line to facilite more stable instruction fetch
54     // performance on benchmarks. Region start is always a branch target, so this also ensures
55     // 16-bytes alignment for branch targets recommended by Intel.
56     // TODO(b/200327919): Try only doing this for heavy-optimized code to avoid extra gaps between
57     // lite-translated regions.
58     current_address_ = AlignUp(current_address_, 64);
59 
60     if (exec_.end() < current_address_ + size) {
61       ResetExecRegion(size);
62     }
63 
64     const uint8_t* result = current_address_;
65     current_address_ += size;
66 
67     code->Install(&exec_, result, &recovery_map_);
68     return AsHostCodeAddr(result);
69   }
70 
FindRecoveryCode(uintptr_t fault_addr)71   [[nodiscard]] uintptr_t FindRecoveryCode(uintptr_t fault_addr) const {
72     std::lock_guard<std::mutex> lock(mutex_);
73     auto it = recovery_map_.find(fault_addr);
74     if (it != recovery_map_.end()) {
75       return it->second;
76     }
77     return 0;
78   }
79 
80   void ResetExecRegion(uint32_t size = ExecRegionFactory::kExecRegionSize) {
81     exec_.Detach();
82     exec_ = ExecRegionFactory::Create(std::max(size, ExecRegionFactory::kExecRegionSize));
83     current_address_ = exec_.begin();
84   }
85 
86  private:
87   ExecRegion exec_;
88   const uint8_t* current_address_ = nullptr;
89   // TODO(b/232598137): have recovery map for each region instead!
90   RecoveryMap recovery_map_;
91   mutable std::mutex mutex_;
92 };
93 
94 // Stored data for generated code.
95 class DataPool {
96  public:
97   // Returns default data pool.
98   static DataPool* GetInstance();
99 
100   DataPool() = default;
101 
102   template <typename T>
Add(const T & v)103   T* Add(const T& v) {
104     return reinterpret_cast<T*>(AddRaw(&v, sizeof(T)));
105   }
106 
107   void* AddRaw(const void* ptr, uint32_t size);
108 
109  private:
110   Arena arena_;
111   std::mutex mutex_;
112 };
113 
114 // Resets exec regions for all CodePools
115 void ResetAllExecRegions();
116 
117 // Returns default code pool.
118 [[nodiscard]] CodePool<ExecRegionAnonymousFactory>* GetDefaultCodePoolInstance();
119 
120 #if defined(__BIONIC__)
121 [[nodiscard]] CodePool<ExecRegionElfBackedFactory>* GetFunctionWrapperCodePoolInstance();
122 #else
123 [[nodiscard]] CodePool<ExecRegionAnonymousFactory>* GetFunctionWrapperCodePoolInstance();
124 #endif
125 
126 }  // namespace berberis
127 
128 #endif  // BERBERIS_RUNTIME_PRIMITIVES_CODE_POOL_H_
129