1 // Copyright 2019, 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 #ifndef VIXL_AARCH64_BENCH_UTILS_H_ 28 #define VIXL_AARCH64_BENCH_UTILS_H_ 29 30 #include <list> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <sys/time.h> 34 #include <vector> 35 36 #include "globals-vixl.h" 37 38 #include "aarch64/macro-assembler-aarch64.h" 39 40 class BenchTimer { 41 public: BenchTimer()42 BenchTimer() { gettimeofday(&start_, NULL); } 43 GetElapsedSeconds() const44 double GetElapsedSeconds() const { 45 timeval elapsed = GetElapsed(); 46 double sec = elapsed.tv_sec; 47 double usec = elapsed.tv_usec; 48 return sec + (usec / 1000000.0); 49 } 50 HasRunFor(uint32_t seconds)51 bool HasRunFor(uint32_t seconds) { 52 timeval elapsed = GetElapsed(); 53 VIXL_ASSERT(elapsed.tv_sec >= 0); 54 return static_cast<uint64_t>(elapsed.tv_sec) >= seconds; 55 } 56 57 private: GetElapsed() const58 timeval GetElapsed() const { 59 VIXL_ASSERT(timerisset(&start_)); 60 timeval now, elapsed; 61 gettimeofday(&now, NULL); 62 timersub(&now, &start_, &elapsed); 63 return elapsed; 64 } 65 66 timeval start_; 67 }; 68 69 // Provide a standard command-line interface for all benchmarks. 70 class BenchCLI { 71 public: 72 // Set default values. BenchCLI(int argc, char* argv[])73 BenchCLI(int argc, char* argv[]) 74 : run_time_(kDefaultRunTime), status_(kRunBenchmark) { 75 for (int i = 1; i < argc; i++) { 76 if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0)) { 77 PrintUsage(argv[0]); 78 status_ = kExitSuccess; 79 return; 80 } 81 } 82 83 // Use the default run time. 84 if (argc == 1) return; 85 86 if (argc != 2) { 87 if (argc > 0) PrintUsage(argv[0]); 88 status_ = kExitFailure; 89 return; 90 } 91 92 char* end; 93 unsigned long run_time = // NOLINT(google-runtime-int) 94 strtoul(argv[1], &end, 0); 95 if ((end == argv[1]) || (run_time > UINT32_MAX)) { 96 PrintUsage(argv[0]); 97 status_ = kExitFailure; 98 return; 99 } 100 run_time_ = static_cast<uint32_t>(run_time); 101 } 102 PrintUsage(char* name)103 void PrintUsage(char* name) { 104 printf("USAGE: %s [OPTIONS]... [RUN_TIME]\n", name); 105 printf("\n"); 106 printf("Run a single VIXL benchmark for approximately RUN_TIME seconds,\n"); 107 printf("or %" PRIu32 " seconds if unspecified.\n", kDefaultRunTime); 108 printf("\n"); 109 #ifdef VIXL_DEBUG 110 printf("This is a DEBUG build. VIXL's assertions will be enabled, and\n"); 111 printf("extra debug information may be printed. The benchmark results\n"); 112 printf("are not representative of expected VIXL deployments.\n"); 113 printf("\n"); 114 #endif 115 printf("OPTIONS:\n"); 116 printf("\n"); 117 printf(" -h, --help\n"); 118 printf(" Print this help message.\n"); 119 } 120 PrintResults(uint64_t iterations, double elapsed_seconds)121 void PrintResults(uint64_t iterations, double elapsed_seconds) { 122 double score = iterations / elapsed_seconds; 123 printf("%g iteration%s per second (%" PRIu64 " / %g)", 124 score, 125 (score == 1.0) ? "" : "s", 126 iterations, 127 elapsed_seconds); 128 #ifdef VIXL_DEBUG 129 printf(" [Warning: DEBUG build]"); 130 #endif 131 printf("\n"); 132 } 133 ShouldExitEarly() const134 bool ShouldExitEarly() const { 135 switch (status_) { 136 case kRunBenchmark: 137 return false; 138 case kExitFailure: 139 case kExitSuccess: 140 return true; 141 } 142 VIXL_UNREACHABLE(); 143 return true; 144 } 145 GetExitCode() const146 int GetExitCode() const { 147 switch (status_) { 148 case kExitFailure: 149 return EXIT_FAILURE; 150 case kExitSuccess: 151 case kRunBenchmark: 152 return EXIT_SUCCESS; 153 } 154 VIXL_UNREACHABLE(); 155 return EXIT_FAILURE; 156 } 157 GetRunTimeInSeconds() const158 uint32_t GetRunTimeInSeconds() const { return run_time_; } 159 160 private: 161 static const uint32_t kDefaultRunTime = 5; 162 163 uint32_t run_time_; 164 165 enum { kRunBenchmark, kExitSuccess, kExitFailure } status_; 166 }; 167 168 // Generate random, but valid (and simulatable) instruction sequences. 169 // 170 // The effect of the generated code is meaningless, but not harmful. That is, 171 // it will not abort, callee-saved registers are properly preserved and so on. 172 // It is possible to call it as a `void fn(void)` function. 173 class BenchCodeGenerator { 174 public: BenchCodeGenerator(vixl::aarch64::MacroAssembler* masm)175 explicit BenchCodeGenerator(vixl::aarch64::MacroAssembler* masm) 176 : masm_(masm), rnd_(0), rnd_bits_(0), call_depth_(0) { 177 // Arbitrarily initialise rand_state_ using the behaviour of srand48(42). 178 rand_state_[2] = 0; 179 rand_state_[1] = 42; 180 rand_state_[0] = 0x330e; 181 } 182 183 void Generate(size_t min_size_in_bytes); 184 185 private: 186 void GeneratePrologue(); 187 void GenerateEpilogue(); 188 189 // Arbitrarily pick one of the other Generate*Sequence() functions. 190 // TODO: Consider allowing this to be biased, so that a benchmark can focus on 191 // a subset of sequences. 192 void GenerateArbitrarySequence(); 193 194 // Instructions with a trivial pass-through to Emit(). 195 void GenerateTrivialSequence(); 196 197 // Instructions using the Operand and MemOperand abstractions. These have a 198 // run-time cost, and many common VIXL APIs use them. 199 void GenerateOperandSequence(); 200 void GenerateMemOperandSequence(); 201 202 // Generate instructions taking immediates that require analysis (and may 203 // result in multiple instructions per macro). 204 void GenerateImmediateSequence(); 205 206 // Immediate-offset and register branches. This also (necessarily) covers adr. 207 void GenerateBranchSequence(); 208 209 // Generate nested, conventional (blr+ret) calls. 210 void GenerateCallReturnSequence(); 211 212 void GenerateFPSequence(); 213 void GenerateNEONSequence(); 214 215 // To exercise veneer pools, GenerateBranchSequence links labels that are 216 // expected to be bound later. This helper binds them. 217 // The Nth youngest label is bound if bit <N> is set in `bind_mask`. That 218 // means that this helper can bind at most 64 pending labels. 219 void BindPendingLabels(uint64_t bind_mask); 220 221 // As above, but unconditionally bind all pending labels (even if there are 222 // more than 64 of them). 223 void BindAllPendingLabels(); 224 225 // Argument selection helpers. These only return caller-saved registers. 226 227 uint64_t GetRandomBits(int bits); PickBool()228 bool PickBool() { return GetRandomBits(1) != 0; } 229 230 unsigned PickRSize(); 231 unsigned PickFPSize(); 232 233 vixl::aarch64::Register PickR(unsigned size_in_bits); 234 vixl::aarch64::VRegister PickV( 235 unsigned size_in_bits = vixl::aarch64::kQRegSize); 236 PickW()237 vixl::aarch64::Register PickW() { return PickR(vixl::aarch64::kWRegSize); } PickX()238 vixl::aarch64::Register PickX() { return PickR(vixl::aarch64::kXRegSize); } PickH()239 vixl::aarch64::VRegister PickH() { return PickV(vixl::aarch64::kHRegSize); } PickS()240 vixl::aarch64::VRegister PickS() { return PickV(vixl::aarch64::kSRegSize); } PickD()241 vixl::aarch64::VRegister PickD() { return PickV(vixl::aarch64::kDRegSize); } 242 243 vixl::aarch64::MacroAssembler* masm_; 244 245 // State for *rand48(), used to randomise code generation. 246 unsigned short rand_state_[3]; // NOLINT(google-runtime-int) 247 248 uint32_t rnd_; 249 int rnd_bits_; 250 251 // The generator can produce nested calls. The probability of it doing this is 252 // influenced by the current call depth, so we have to track it here. 253 int call_depth_; 254 255 struct LabelPair { 256 // We can't copy labels, so we have to allocate them dynamically to store 257 // them in a std::list. 258 vixl::aarch64::Label* target; 259 vixl::aarch64::Label* cont; 260 }; 261 std::list<LabelPair> labels_; 262 263 // Some sequences need a scratch area. Space for this is allocated on the 264 // stack, and stored in this register. 265 static const vixl::aarch64::Register scratch; 266 }; 267 268 #endif // VIXL_AARCH64_BENCH_UTILS_H_ 269