You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
8.5 KiB

// Copyright 2019, VIXL authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of ARM Limited nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef VIXL_AARCH64_BENCH_UTILS_H_
#define VIXL_AARCH64_BENCH_UTILS_H_
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <list>
#include <vector>
#include "globals-vixl.h"
#include "aarch64/macro-assembler-aarch64.h"
class BenchTimer {
public:
BenchTimer() { gettimeofday(&start_, NULL); }
double GetElapsedSeconds() const {
timeval elapsed = GetElapsed();
double sec = elapsed.tv_sec;
double usec = elapsed.tv_usec;
return sec + (usec / 1000000.0);
}
bool HasRunFor(uint32_t seconds) {
timeval elapsed = GetElapsed();
VIXL_ASSERT(elapsed.tv_sec >= 0);
return static_cast<uint64_t>(elapsed.tv_sec) >= seconds;
}
private:
timeval GetElapsed() const {
VIXL_ASSERT(timerisset(&start_));
timeval now, elapsed;
gettimeofday(&now, NULL);
timersub(&now, &start_, &elapsed);
return elapsed;
}
timeval start_;
};
// Provide a standard command-line interface for all benchmarks.
class BenchCLI {
public:
// Set default values.
BenchCLI(int argc, char* argv[])
: run_time_(kDefaultRunTime), status_(kRunBenchmark) {
for (int i = 1; i < argc; i++) {
if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0)) {
PrintUsage(argv[0]);
status_ = kExitSuccess;
return;
}
}
// Use the default run time.
if (argc == 1) return;
if (argc != 2) {
if (argc > 0) PrintUsage(argv[0]);
status_ = kExitFailure;
return;
}
char* end;
unsigned long run_time = strtoul(argv[1], &end, 0); // NOLINT(runtime/int)
if ((end == argv[1]) || (run_time > UINT32_MAX)) {
PrintUsage(argv[0]);
status_ = kExitFailure;
return;
}
run_time_ = static_cast<uint32_t>(run_time);
}
void PrintUsage(char* name) {
printf("USAGE: %s [OPTIONS]... [RUN_TIME]\n", name);
printf("\n");
printf("Run a single VIXL benchmark for approximately RUN_TIME seconds,\n");
printf("or %" PRIu32 " seconds if unspecified.\n", kDefaultRunTime);
printf("\n");
#ifdef VIXL_DEBUG
printf("This is a DEBUG build. VIXL's assertions will be enabled, and\n");
printf("extra debug information may be printed. The benchmark results\n");
printf("are not representative of expected VIXL deployments.\n");
printf("\n");
#endif
printf("OPTIONS:\n");
printf("\n");
printf(" -h, --help\n");
printf(" Print this help message.\n");
}
void PrintResults(uint64_t iterations, double elapsed_seconds) {
double score = iterations / elapsed_seconds;
printf("%g iteration%s per second (%" PRIu64 " / %g)",
score,
(score == 1.0) ? "" : "s",
iterations,
elapsed_seconds);
#ifdef VIXL_DEBUG
printf(" [Warning: DEBUG build]");
#endif
printf("\n");
}
bool ShouldExitEarly() const {
switch (status_) {
case kRunBenchmark:
return false;
case kExitFailure:
case kExitSuccess:
return true;
}
VIXL_UNREACHABLE();
return true;
}
int GetExitCode() const {
switch (status_) {
case kExitFailure:
return EXIT_FAILURE;
case kExitSuccess:
case kRunBenchmark:
return EXIT_SUCCESS;
}
VIXL_UNREACHABLE();
return EXIT_FAILURE;
}
uint32_t GetRunTimeInSeconds() const { return run_time_; }
private:
static const uint32_t kDefaultRunTime = 5;
uint32_t run_time_;
enum { kRunBenchmark, kExitSuccess, kExitFailure } status_;
};
// Generate random, but valid (and simulatable) instruction sequences.
//
// The effect of the generated code is meaningless, but not harmful. That is,
// it will not abort, callee-saved registers are properly preserved and so on.
// It is possible to call it as a `void fn(void)` function.
class BenchCodeGenerator {
public:
explicit BenchCodeGenerator(vixl::aarch64::MacroAssembler* masm)
: masm_(masm), rnd_(0), rnd_bits_(0), call_depth_(0) {
// Arbitrarily initialise rand_state_ using the behaviour of srand48(42).
rand_state_[2] = 0;
rand_state_[1] = 42;
rand_state_[0] = 0x330e;
}
void Generate(size_t min_size_in_bytes);
private:
void GeneratePrologue();
void GenerateEpilogue();
// Arbitrarily pick one of the other Generate*Sequence() functions.
// TODO: Consider allowing this to be biased, so that a benchmark can focus on
// a subset of sequences.
void GenerateArbitrarySequence();
// Instructions with a trivial pass-through to Emit().
void GenerateTrivialSequence();
// Instructions using the Operand and MemOperand abstractions. These have a
// run-time cost, and many common VIXL APIs use them.
void GenerateOperandSequence();
void GenerateMemOperandSequence();
// Generate instructions taking immediates that require analysis (and may
// result in multiple instructions per macro).
void GenerateImmediateSequence();
// Immediate-offset and register branches. This also (necessarily) covers adr.
void GenerateBranchSequence();
// Generate nested, conventional (blr+ret) calls.
void GenerateCallReturnSequence();
void GenerateFPSequence();
void GenerateNEONSequence();
// To exercise veneer pools, GenerateBranchSequence links labels that are
// expected to be bound later. This helper binds them.
// The Nth youngest label is bound if bit <N> is set in `bind_mask`. That
// means that this helper can bind at most 64 pending labels.
void BindPendingLabels(uint64_t bind_mask);
// As above, but unconditionally bind all pending labels (even if there are
// more than 64 of them).
void BindAllPendingLabels();
// Argument selection helpers. These only return caller-saved registers.
uint64_t GetRandomBits(int bits);
bool PickBool() { return GetRandomBits(1) != 0; }
unsigned PickRSize();
unsigned PickFPSize();
vixl::aarch64::Register PickR(unsigned size_in_bits);
vixl::aarch64::VRegister PickV(
unsigned size_in_bits = vixl::aarch64::kQRegSize);
vixl::aarch64::Register PickW() { return PickR(vixl::aarch64::kWRegSize); }
vixl::aarch64::Register PickX() { return PickR(vixl::aarch64::kXRegSize); }
vixl::aarch64::VRegister PickH() { return PickV(vixl::aarch64::kHRegSize); }
vixl::aarch64::VRegister PickS() { return PickV(vixl::aarch64::kSRegSize); }
vixl::aarch64::VRegister PickD() { return PickV(vixl::aarch64::kDRegSize); }
vixl::aarch64::MacroAssembler* masm_;
// State for *rand48(), used to randomise code generation.
unsigned short rand_state_[3]; // NOLINT(runtime/int)
uint32_t rnd_;
int rnd_bits_;
// The generator can produce nested calls. The probability of it doing this is
// influenced by the current call depth, so we have to track it here.
int call_depth_;
struct LabelPair {
// We can't copy labels, so we have to allocate them dynamically to store
// them in a std::list.
vixl::aarch64::Label* target;
vixl::aarch64::Label* cont;
};
std::list<LabelPair> labels_;
// Some sequences need a scratch area. Space for this is allocated on the
// stack, and stored in this register.
static const vixl::aarch64::Register scratch;
};
#endif // VIXL_AARCH64_BENCH_UTILS_H_