Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 1 | // Copyright 2016, VIXL authors |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are met: |
| 6 | // |
| 7 | // * Redistributions of source code must retain the above copyright notice, |
| 8 | // this list of conditions and the following disclaimer. |
| 9 | // * Redistributions in binary form must reproduce the above copyright notice, |
| 10 | // this list of conditions and the following disclaimer in the documentation |
| 11 | // and/or other materials provided with the distribution. |
| 12 | // * Neither the name of ARM Limited nor the names of its contributors may be |
| 13 | // used to endorse or promote products derived from this software without |
| 14 | // specific prior written permission. |
| 15 | // |
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND |
| 17 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
| 20 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 21 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 22 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 23 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 24 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 25 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | |
| 27 | |
| 28 | #ifndef VIXL_CODE_GENERATION_SCOPES_H_ |
| 29 | #define VIXL_CODE_GENERATION_SCOPES_H_ |
| 30 | |
| 31 | |
| 32 | #include "assembler-base-vixl.h" |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 33 | #include "macro-assembler-interface.h" |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 34 | |
| 35 | |
| 36 | namespace vixl { |
| 37 | |
| 38 | // This scope will: |
| 39 | // - Allow code emission from the specified `Assembler`. |
| 40 | // - Optionally reserve space in the `CodeBuffer` (if it is managed by VIXL). |
| 41 | // - Optionally, on destruction, check the size of the generated code. |
| 42 | // (The size can be either exact or a maximum size.) |
| 43 | class CodeBufferCheckScope { |
| 44 | public: |
| 45 | // Tell whether or not the scope needs to ensure the associated CodeBuffer |
| 46 | // has enough space for the requested size. |
| 47 | enum BufferSpacePolicy { |
| 48 | kReserveBufferSpace, |
| 49 | kDontReserveBufferSpace, |
| 50 | |
| 51 | // Deprecated, but kept for backward compatibility. |
| 52 | kCheck = kReserveBufferSpace, |
| 53 | kNoCheck = kDontReserveBufferSpace |
| 54 | }; |
| 55 | |
| 56 | // Tell whether or not the scope should assert the amount of code emitted |
| 57 | // within the scope is consistent with the requested amount. |
| 58 | enum SizePolicy { |
| 59 | kNoAssert, // Do not check the size of the code emitted. |
| 60 | kExactSize, // The code emitted must be exactly size bytes. |
| 61 | kMaximumSize // The code emitted must be at most size bytes. |
| 62 | }; |
| 63 | |
| 64 | // This constructor implicitly calls `Open` to initialise the scope |
| 65 | // (`assembler` must not be `NULL`), so it is ready to use immediately after |
| 66 | // it has been constructed. |
| 67 | CodeBufferCheckScope(internal::AssemblerBase* assembler, |
| 68 | size_t size, |
| 69 | BufferSpacePolicy check_policy = kReserveBufferSpace, |
| 70 | SizePolicy size_policy = kMaximumSize) |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 71 | : assembler_(NULL), initialised_(false) { |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 72 | Open(assembler, size, check_policy, size_policy); |
| 73 | } |
| 74 | |
| 75 | // This constructor does not implicitly initialise the scope. Instead, the |
| 76 | // user is required to explicitly call the `Open` function before using the |
| 77 | // scope. |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 78 | CodeBufferCheckScope() : assembler_(NULL), initialised_(false) { |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 79 | // Nothing to do. |
| 80 | } |
| 81 | |
| 82 | virtual ~CodeBufferCheckScope() { Close(); } |
| 83 | |
| 84 | // This function performs the actual initialisation work. |
| 85 | void Open(internal::AssemblerBase* assembler, |
| 86 | size_t size, |
| 87 | BufferSpacePolicy check_policy = kReserveBufferSpace, |
| 88 | SizePolicy size_policy = kMaximumSize) { |
| 89 | VIXL_ASSERT(!initialised_); |
| 90 | VIXL_ASSERT(assembler != NULL); |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 91 | assembler_ = assembler; |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 92 | if (check_policy == kReserveBufferSpace) { |
| 93 | assembler->GetBuffer()->EnsureSpaceFor(size); |
| 94 | } |
| 95 | #ifdef VIXL_DEBUG |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 96 | limit_ = assembler_->GetSizeOfCodeGenerated() + size; |
| 97 | assert_policy_ = size_policy; |
| 98 | previous_allow_assembler_ = assembler_->AllowAssembler(); |
| 99 | assembler_->SetAllowAssembler(true); |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 100 | #else |
| 101 | USE(size_policy); |
| 102 | #endif |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 103 | initialised_ = true; |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | // This function performs the cleaning-up work. It must succeed even if the |
| 107 | // scope has not been opened. It is safe to call multiple times. |
| 108 | void Close() { |
| 109 | #ifdef VIXL_DEBUG |
| 110 | if (!initialised_) { |
| 111 | return; |
| 112 | } |
| 113 | assembler_->SetAllowAssembler(previous_allow_assembler_); |
| 114 | switch (assert_policy_) { |
| 115 | case kNoAssert: |
| 116 | break; |
| 117 | case kExactSize: |
| 118 | VIXL_ASSERT(assembler_->GetSizeOfCodeGenerated() == limit_); |
| 119 | break; |
| 120 | case kMaximumSize: |
| 121 | VIXL_ASSERT(assembler_->GetSizeOfCodeGenerated() <= limit_); |
| 122 | break; |
| 123 | default: |
| 124 | VIXL_UNREACHABLE(); |
| 125 | } |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 126 | #endif |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 127 | initialised_ = false; |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | protected: |
| 131 | internal::AssemblerBase* assembler_; |
| 132 | SizePolicy assert_policy_; |
| 133 | size_t limit_; |
| 134 | bool previous_allow_assembler_; |
| 135 | bool initialised_; |
| 136 | }; |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 137 | |
| 138 | |
| 139 | // This scope will: |
| 140 | // - Do the same as `CodeBufferCheckSCope`, but: |
| 141 | // - If managed by VIXL, always reserve space in the `CodeBuffer`. |
| 142 | // - Always check the size (exact or maximum) of the generated code on |
| 143 | // destruction. |
| 144 | // - Emit pools if the specified size would push them out of range. |
| 145 | // - Block pools emission for the duration of the scope. |
| 146 | // This scope allows the `Assembler` and `MacroAssembler` to be freely and |
| 147 | // safely mixed for its duration. |
| 148 | class EmissionCheckScope : public CodeBufferCheckScope { |
| 149 | public: |
| 150 | // This constructor implicitly calls `Open` (when `masm` is not `NULL`) to |
| 151 | // initialise the scope, so it is ready to use immediately after it has been |
| 152 | // constructed. |
| 153 | EmissionCheckScope(MacroAssemblerInterface* masm, |
| 154 | size_t size, |
| 155 | SizePolicy size_policy = kMaximumSize) { |
| 156 | Open(masm, size, size_policy); |
| 157 | } |
| 158 | |
| 159 | // This constructor does not implicitly initialise the scope. Instead, the |
| 160 | // user is required to explicitly call the `Open` function before using the |
| 161 | // scope. |
| 162 | EmissionCheckScope() {} |
| 163 | |
| 164 | virtual ~EmissionCheckScope() { Close(); } |
| 165 | |
Pierre Langlois | d56f609 | 2017-01-11 14:00:39 +0000 | [diff] [blame] | 166 | enum PoolPolicy { |
| 167 | // Do not forbid pool emission inside the scope. Pools will not be emitted |
| 168 | // on `Open` either. |
| 169 | kIgnorePools, |
| 170 | // Force pools to be generated on `Open` if necessary and block their |
| 171 | // emission inside the scope. |
| 172 | kBlockPools, |
| 173 | // Deprecated, but kept for backward compatibility. |
| 174 | kCheckPools = kBlockPools |
| 175 | }; |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 176 | |
| 177 | void Open(MacroAssemblerInterface* masm, |
| 178 | size_t size, |
| 179 | SizePolicy size_policy = kMaximumSize) { |
Pierre Langlois | d56f609 | 2017-01-11 14:00:39 +0000 | [diff] [blame] | 180 | Open(masm, size, size_policy, kBlockPools); |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | void Close() { |
| 184 | if (!initialised_) { |
| 185 | return; |
| 186 | } |
| 187 | if (masm_ == NULL) { |
| 188 | // Nothing to do. |
| 189 | return; |
| 190 | } |
Pierre Langlois | 6ee0978 | 2017-01-09 18:33:13 +0000 | [diff] [blame] | 191 | // Perform the opposite of `Open`, which is: |
| 192 | // - Check the code generation limit was not exceeded. |
| 193 | // - Release the pools. |
| 194 | CodeBufferCheckScope::Close(); |
Pierre Langlois | d56f609 | 2017-01-11 14:00:39 +0000 | [diff] [blame] | 195 | if (pool_policy_ == kBlockPools) { |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 196 | masm_->ReleasePools(); |
| 197 | } |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 198 | VIXL_ASSERT(!initialised_); |
| 199 | } |
| 200 | |
| 201 | protected: |
| 202 | void Open(MacroAssemblerInterface* masm, |
| 203 | size_t size, |
| 204 | SizePolicy size_policy, |
| 205 | PoolPolicy pool_policy) { |
| 206 | if (masm == NULL) { |
| 207 | // Nothing to do. |
| 208 | // We may reach this point in a context of conditional code generation. |
| 209 | // See `aarch64::MacroAssembler::MoveImmediateHelper()` for an example. |
| 210 | return; |
| 211 | } |
| 212 | masm_ = masm; |
| 213 | pool_policy_ = pool_policy; |
Pierre Langlois | d56f609 | 2017-01-11 14:00:39 +0000 | [diff] [blame] | 214 | if (pool_policy_ == kBlockPools) { |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 215 | // To avoid duplicating the work to check that enough space is available |
| 216 | // in the buffer, do not use the more generic `EnsureEmitFor()`. It is |
| 217 | // done below when opening `CodeBufferCheckScope`. |
| 218 | masm->EnsureEmitPoolsFor(size); |
| 219 | masm->BlockPools(); |
| 220 | } |
| 221 | // The buffer should be checked *after* we emit the pools. |
Alexandre Rames | 8d191ab | 2016-11-29 11:23:27 +0000 | [diff] [blame] | 222 | CodeBufferCheckScope::Open(masm->AsAssemblerBase(), |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 223 | size, |
| 224 | kReserveBufferSpace, |
| 225 | size_policy); |
| 226 | VIXL_ASSERT(initialised_); |
| 227 | } |
| 228 | |
| 229 | // This constructor should only be used from code that is *currently |
| 230 | // generating* the pools, to avoid an infinite loop. |
| 231 | EmissionCheckScope(MacroAssemblerInterface* masm, |
| 232 | size_t size, |
| 233 | SizePolicy size_policy, |
| 234 | PoolPolicy pool_policy) { |
| 235 | Open(masm, size, size_policy, pool_policy); |
| 236 | } |
| 237 | |
| 238 | MacroAssemblerInterface* masm_; |
| 239 | PoolPolicy pool_policy_; |
| 240 | }; |
| 241 | |
Alexandre Rames | 07d1aa5 | 2016-10-25 17:20:51 +0100 | [diff] [blame] | 242 | // Use this scope when you need a one-to-one mapping between methods and |
| 243 | // instructions. This scope will: |
| 244 | // - Do the same as `EmissionCheckScope`. |
| 245 | // - Block access to the MacroAssemblerInterface (using run-time assertions). |
| 246 | class ExactAssemblyScope : public EmissionCheckScope { |
| 247 | public: |
| 248 | // This constructor implicitly calls `Open` (when `masm` is not `NULL`) to |
| 249 | // initialise the scope, so it is ready to use immediately after it has been |
| 250 | // constructed. |
| 251 | ExactAssemblyScope(MacroAssemblerInterface* masm, |
| 252 | size_t size, |
Pierre Langlois | 45262d6 | 2017-01-12 15:27:32 +0000 | [diff] [blame] | 253 | SizePolicy size_policy = kExactSize) { |
| 254 | Open(masm, size, size_policy); |
Alexandre Rames | 07d1aa5 | 2016-10-25 17:20:51 +0100 | [diff] [blame] | 255 | } |
| 256 | |
| 257 | // This constructor does not implicitly initialise the scope. Instead, the |
| 258 | // user is required to explicitly call the `Open` function before using the |
| 259 | // scope. |
| 260 | ExactAssemblyScope() {} |
| 261 | |
Pierre Langlois | 45262d6 | 2017-01-12 15:27:32 +0000 | [diff] [blame] | 262 | virtual ~ExactAssemblyScope() { Close(); } |
| 263 | |
| 264 | void Open(MacroAssemblerInterface* masm, |
| 265 | size_t size, |
| 266 | SizePolicy size_policy = kExactSize) { |
| 267 | Open(masm, size, size_policy, kBlockPools); |
| 268 | } |
| 269 | |
| 270 | void Close() { |
| 271 | if (!initialised_) { |
| 272 | return; |
| 273 | } |
| 274 | if (masm_ == NULL) { |
| 275 | // Nothing to do. |
| 276 | return; |
| 277 | } |
Alexandre Rames | 07d1aa5 | 2016-10-25 17:20:51 +0100 | [diff] [blame] | 278 | #ifdef VIXL_DEBUG |
Scott Wakeling | c521a8b | 2016-11-17 14:42:07 +0000 | [diff] [blame] | 279 | masm_->SetAllowMacroInstructions(previous_allow_macro_assembler_); |
Pierre Langlois | 45262d6 | 2017-01-12 15:27:32 +0000 | [diff] [blame] | 280 | #else |
| 281 | USE(previous_allow_macro_assembler_); |
Alexandre Rames | 07d1aa5 | 2016-10-25 17:20:51 +0100 | [diff] [blame] | 282 | #endif |
Pierre Langlois | 45262d6 | 2017-01-12 15:27:32 +0000 | [diff] [blame] | 283 | EmissionCheckScope::Close(); |
Alexandre Rames | 07d1aa5 | 2016-10-25 17:20:51 +0100 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | protected: |
| 287 | // This protected constructor allows overriding the pool policy. It is |
| 288 | // available to allow this scope to be used in code that handles generation |
| 289 | // of pools. |
| 290 | ExactAssemblyScope(MacroAssemblerInterface* masm, |
| 291 | size_t size, |
| 292 | SizePolicy assert_policy, |
Pierre Langlois | 45262d6 | 2017-01-12 15:27:32 +0000 | [diff] [blame] | 293 | PoolPolicy pool_policy) { |
| 294 | Open(masm, size, assert_policy, pool_policy); |
| 295 | } |
| 296 | |
| 297 | void Open(MacroAssemblerInterface* masm, |
| 298 | size_t size, |
| 299 | SizePolicy size_policy, |
| 300 | PoolPolicy pool_policy) { |
| 301 | VIXL_ASSERT(size_policy != kNoAssert); |
| 302 | if (masm == NULL) { |
| 303 | // Nothing to do. |
| 304 | return; |
| 305 | } |
| 306 | // Rely on EmissionCheckScope::Open to initialise `masm_` and |
| 307 | // `pool_policy_`. |
| 308 | EmissionCheckScope::Open(masm, size, size_policy, pool_policy); |
Alexandre Rames | 07d1aa5 | 2016-10-25 17:20:51 +0100 | [diff] [blame] | 309 | #ifdef VIXL_DEBUG |
| 310 | previous_allow_macro_assembler_ = masm->AllowMacroInstructions(); |
| 311 | masm->SetAllowMacroInstructions(false); |
| 312 | #endif |
| 313 | } |
| 314 | |
| 315 | private: |
| 316 | bool previous_allow_macro_assembler_; |
| 317 | }; |
| 318 | |
Alexandre Rames | c0b25f2 | 2016-10-19 13:53:55 +0100 | [diff] [blame] | 319 | |
Alexandre Rames | 9dd6fa3 | 2016-10-12 13:26:54 +0100 | [diff] [blame] | 320 | } // namespace vixl |
| 321 | |
| 322 | #endif // VIXL_CODE_GENERATION_SCOPES_H_ |