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