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