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.
1569 lines
56 KiB
1569 lines
56 KiB
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
|
|
#define ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
|
|
|
|
#include "assembler.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
|
|
#include "base/malloc_arena_pool.h"
|
|
#include "assembler_test_base.h"
|
|
#include "common_runtime_test.h" // For ScratchFile
|
|
|
|
namespace art {
|
|
|
|
// Helper for a constexpr string length.
|
|
constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
|
|
return ('\0' == str[0]) ? count : ConstexprStrLen(str+1, count+1);
|
|
}
|
|
|
|
enum class RegisterView { // private
|
|
kUsePrimaryName,
|
|
kUseSecondaryName,
|
|
kUseTertiaryName,
|
|
kUseQuaternaryName,
|
|
};
|
|
|
|
// For use in the template as the default type to get a nonvector registers version.
|
|
struct NoVectorRegs {};
|
|
|
|
template<typename Ass,
|
|
typename Addr,
|
|
typename Reg,
|
|
typename FPReg,
|
|
typename Imm,
|
|
typename VecReg = NoVectorRegs>
|
|
class AssemblerTest : public AssemblerTestBase {
|
|
public:
|
|
Ass* GetAssembler() {
|
|
return assembler_.get();
|
|
}
|
|
|
|
typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler);
|
|
|
|
void DriverFn(TestFn f, const std::string& test_name) {
|
|
DriverWrapper(f(this, assembler_.get()), test_name);
|
|
}
|
|
|
|
// This driver assumes the assembler has already been called.
|
|
void DriverStr(const std::string& assembly_string, const std::string& test_name) {
|
|
DriverWrapper(assembly_string, test_name);
|
|
}
|
|
|
|
//
|
|
// Register repeats.
|
|
//
|
|
|
|
std::string RepeatR(void (Ass::*f)(Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegister<Reg>(f,
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string Repeatr(void (Ass::*f)(Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegister<Reg>(f,
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatRR(void (Ass::*f)(Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatRRNoDupes(void (Ass::*f)(Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegistersNoDupes<Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string Repeatrr(void (Ass::*f)(Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string Repeatww(void (Ass::*f)(Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string Repeatbb(void (Ass::*f)(Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatRRR(void (Ass::*f)(Reg, Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string Repeatrb(void (Ass::*f)(Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatRr(void (Ass::*f)(Reg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, Reg>(f,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatRI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) {
|
|
return RepeatRegisterImm<RegisterView::kUsePrimaryName>(f, imm_bytes, fmt);
|
|
}
|
|
|
|
std::string RepeatrI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) {
|
|
return RepeatRegisterImm<RegisterView::kUseSecondaryName>(f, imm_bytes, fmt);
|
|
}
|
|
|
|
std::string RepeatwI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) {
|
|
return RepeatRegisterImm<RegisterView::kUseTertiaryName>(f, imm_bytes, fmt);
|
|
}
|
|
|
|
std::string RepeatbI(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, const std::string& fmt) {
|
|
return RepeatRegisterImm<RegisterView::kUseQuaternaryName>(f, imm_bytes, fmt);
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2, typename ImmType>
|
|
std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, ImmType),
|
|
int imm_bits,
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
std::string (AssemblerTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerTest::*GetName2)(const Reg2&),
|
|
const std::string& fmt,
|
|
int bias = 0,
|
|
int multiplier = 1) {
|
|
std::string str;
|
|
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
|
|
|
|
for (auto reg1 : reg1_registers) {
|
|
for (auto reg2 : reg2_registers) {
|
|
for (int64_t imm : imms) {
|
|
ImmType new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg1, *reg2, new_imm * multiplier + bias);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
|
|
base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
|
|
base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm * multiplier + bias;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2, typename Reg3, typename ImmType>
|
|
std::string RepeatTemplatedRegistersImmBits(void (Ass::*f)(Reg1, Reg2, Reg3, ImmType),
|
|
int imm_bits,
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
const std::vector<Reg3*> reg3_registers,
|
|
std::string (AssemblerTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerTest::*GetName2)(const Reg2&),
|
|
std::string (AssemblerTest::*GetName3)(const Reg3&),
|
|
std::string fmt,
|
|
int bias) {
|
|
std::string str;
|
|
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
|
|
|
|
for (auto reg1 : reg1_registers) {
|
|
for (auto reg2 : reg2_registers) {
|
|
for (auto reg3 : reg3_registers) {
|
|
for (int64_t imm : imms) {
|
|
ImmType new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg1, *reg2, *reg3, new_imm + bias);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
|
|
base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
|
|
base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
std::string reg3_string = (this->*GetName3)(*reg3);
|
|
size_t reg3_index;
|
|
while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
|
|
base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
|
|
}
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm + bias;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename ImmType, typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedImmBitsRegisters(void (Ass::*f)(ImmType, Reg1, Reg2),
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
std::string (AssemblerTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerTest::*GetName2)(const Reg2&),
|
|
int imm_bits,
|
|
const std::string& fmt) {
|
|
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
|
|
|
|
WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size());
|
|
|
|
std::string str;
|
|
for (auto reg1 : reg1_registers) {
|
|
for (auto reg2 : reg2_registers) {
|
|
for (int64_t imm : imms) {
|
|
ImmType new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(new_imm, *reg1, *reg2);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
|
|
base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
|
|
base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename RegType, typename ImmType>
|
|
std::string RepeatTemplatedRegisterImmBits(void (Ass::*f)(RegType, ImmType),
|
|
int imm_bits,
|
|
const std::vector<RegType*> registers,
|
|
std::string (AssemblerTest::*GetName)(const RegType&),
|
|
const std::string& fmt,
|
|
int bias) {
|
|
std::string str;
|
|
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
|
|
|
|
for (auto reg : registers) {
|
|
for (int64_t imm : imms) {
|
|
ImmType new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg, new_imm + bias);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg_string = (this->*GetName)(*reg);
|
|
size_t reg_index;
|
|
while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
|
|
base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
|
|
}
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm + bias;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatRRIb(void (Ass::*f)(Reg, Reg, ImmType),
|
|
int imm_bits,
|
|
const std::string& fmt,
|
|
int bias = 0) {
|
|
return RepeatTemplatedRegistersImmBits<Reg, Reg, ImmType>(f,
|
|
imm_bits,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt,
|
|
bias);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatRRRIb(void (Ass::*f)(Reg, Reg, Reg, ImmType),
|
|
int imm_bits,
|
|
const std::string& fmt,
|
|
int bias = 0) {
|
|
return RepeatTemplatedRegistersImmBits<Reg, Reg, Reg, ImmType>(f,
|
|
imm_bits,
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt,
|
|
bias);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatRIb(void (Ass::*f)(Reg, ImmType), int imm_bits, std::string fmt, int bias = 0) {
|
|
return RepeatTemplatedRegisterImmBits<Reg, ImmType>(f,
|
|
imm_bits,
|
|
GetRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt,
|
|
bias);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatFRIb(void (Ass::*f)(FPReg, Reg, ImmType),
|
|
int imm_bits,
|
|
const std::string& fmt,
|
|
int bias = 0) {
|
|
return RepeatTemplatedRegistersImmBits<FPReg, Reg, ImmType>(f,
|
|
imm_bits,
|
|
GetFPRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt,
|
|
bias);
|
|
}
|
|
|
|
std::string RepeatFF(void (Ass::*f)(FPReg, FPReg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<FPReg, FPReg>(f,
|
|
GetFPRegisters(),
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetFPRegName,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatFFF(void (Ass::*f)(FPReg, FPReg, FPReg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<FPReg, FPReg, FPReg>(f,
|
|
GetFPRegisters(),
|
|
GetFPRegisters(),
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetFPRegName,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatFFR(void (Ass::*f)(FPReg, FPReg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<FPReg, FPReg, Reg>(
|
|
f,
|
|
GetFPRegisters(),
|
|
GetFPRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatFFI(void (Ass::*f)(FPReg, FPReg, const Imm&),
|
|
size_t imm_bytes,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedRegistersImm<FPReg, FPReg>(f,
|
|
GetFPRegisters(),
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetFPRegName,
|
|
imm_bytes,
|
|
fmt);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatFFIb(void (Ass::*f)(FPReg, FPReg, ImmType),
|
|
int imm_bits,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedRegistersImmBits<FPReg, FPReg, ImmType>(f,
|
|
imm_bits,
|
|
GetFPRegisters(),
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetFPRegName,
|
|
fmt);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatIbFF(void (Ass::*f)(ImmType, FPReg, FPReg),
|
|
int imm_bits,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedImmBitsRegisters<ImmType, FPReg, FPReg>(f,
|
|
GetFPRegisters(),
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetFPRegName,
|
|
imm_bits,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatFR(void (Ass::*f)(FPReg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<FPReg, Reg>(f,
|
|
GetFPRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatFr(void (Ass::*f)(FPReg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<FPReg, Reg>(f,
|
|
GetFPRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatRF(void (Ass::*f)(Reg, FPReg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, FPReg>(f,
|
|
GetRegisters(),
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetFPRegName,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatrF(void (Ass::*f)(Reg, FPReg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<Reg, FPReg>(f,
|
|
GetRegisters(),
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
&AssemblerTest::GetFPRegName,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatI(void (Ass::*f)(const Imm&),
|
|
size_t imm_bytes,
|
|
const std::string& fmt,
|
|
bool as_uint = false) {
|
|
std::string str;
|
|
std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint);
|
|
|
|
WarnOnCombinations(imms.size());
|
|
|
|
for (int64_t imm : imms) {
|
|
Imm new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(new_imm);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
std::string RepeatVV(void (Ass::*f)(VecReg, VecReg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<VecReg, VecReg>(f,
|
|
GetVectorRegisters(),
|
|
GetVectorRegisters(),
|
|
&AssemblerTest::GetVecRegName,
|
|
&AssemblerTest::GetVecRegName,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatVVV(void (Ass::*f)(VecReg, VecReg, VecReg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<VecReg, VecReg, VecReg>(f,
|
|
GetVectorRegisters(),
|
|
GetVectorRegisters(),
|
|
GetVectorRegisters(),
|
|
&AssemblerTest::GetVecRegName,
|
|
&AssemblerTest::GetVecRegName,
|
|
&AssemblerTest::GetVecRegName,
|
|
fmt);
|
|
}
|
|
|
|
std::string RepeatVR(void (Ass::*f)(VecReg, Reg), const std::string& fmt) {
|
|
return RepeatTemplatedRegisters<VecReg, Reg>(
|
|
f,
|
|
GetVectorRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetVecRegName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatVIb(void (Ass::*f)(VecReg, ImmType),
|
|
int imm_bits,
|
|
std::string fmt,
|
|
int bias = 0) {
|
|
return RepeatTemplatedRegisterImmBits<VecReg, ImmType>(f,
|
|
imm_bits,
|
|
GetVectorRegisters(),
|
|
&AssemblerTest::GetVecRegName,
|
|
fmt,
|
|
bias);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatVRIb(void (Ass::*f)(VecReg, Reg, ImmType),
|
|
int imm_bits,
|
|
const std::string& fmt,
|
|
int bias = 0,
|
|
int multiplier = 1) {
|
|
return RepeatTemplatedRegistersImmBits<VecReg, Reg, ImmType>(
|
|
f,
|
|
imm_bits,
|
|
GetVectorRegisters(),
|
|
GetRegisters(),
|
|
&AssemblerTest::GetVecRegName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt,
|
|
bias,
|
|
multiplier);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatRVIb(void (Ass::*f)(Reg, VecReg, ImmType),
|
|
int imm_bits,
|
|
const std::string& fmt,
|
|
int bias = 0,
|
|
int multiplier = 1) {
|
|
return RepeatTemplatedRegistersImmBits<Reg, VecReg, ImmType>(
|
|
f,
|
|
imm_bits,
|
|
GetRegisters(),
|
|
GetVectorRegisters(),
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetVecRegName,
|
|
fmt,
|
|
bias,
|
|
multiplier);
|
|
}
|
|
|
|
template <typename ImmType>
|
|
std::string RepeatVVIb(void (Ass::*f)(VecReg, VecReg, ImmType),
|
|
int imm_bits,
|
|
const std::string& fmt,
|
|
int bias = 0) {
|
|
return RepeatTemplatedRegistersImmBits<VecReg, VecReg, ImmType>(f,
|
|
imm_bits,
|
|
GetVectorRegisters(),
|
|
GetVectorRegisters(),
|
|
&AssemblerTest::GetVecRegName,
|
|
&AssemblerTest::GetVecRegName,
|
|
fmt,
|
|
bias);
|
|
}
|
|
|
|
// The following functions are public so that TestFn can use them...
|
|
|
|
// Returns a vector of address used by any of the repeat methods
|
|
// involving an "A" (e.g. RepeatA).
|
|
virtual std::vector<Addr> GetAddresses() = 0;
|
|
|
|
// Returns a vector of registers used by any of the repeat methods
|
|
// involving an "R" (e.g. RepeatR).
|
|
virtual std::vector<Reg*> GetRegisters() = 0;
|
|
|
|
// Returns a vector of fp-registers used by any of the repeat methods
|
|
// involving an "F" (e.g. RepeatFF).
|
|
virtual std::vector<FPReg*> GetFPRegisters() {
|
|
UNIMPLEMENTED(FATAL) << "Architecture does not support floating-point registers";
|
|
UNREACHABLE();
|
|
}
|
|
|
|
// Returns a vector of dedicated simd-registers used by any of the repeat
|
|
// methods involving an "V" (e.g. RepeatVV).
|
|
virtual std::vector<VecReg*> GetVectorRegisters() {
|
|
UNIMPLEMENTED(FATAL) << "Architecture does not support vector registers";
|
|
UNREACHABLE();
|
|
}
|
|
|
|
// Secondary register names are the secondary view on registers, e.g., 32b on 64b systems.
|
|
virtual std::string GetSecondaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
|
|
UNIMPLEMENTED(FATAL) << "Architecture does not support secondary registers";
|
|
UNREACHABLE();
|
|
}
|
|
|
|
// Tertiary register names are the tertiary view on registers, e.g., 16b on 64b systems.
|
|
virtual std::string GetTertiaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
|
|
UNIMPLEMENTED(FATAL) << "Architecture does not support tertiary registers";
|
|
UNREACHABLE();
|
|
}
|
|
|
|
// Quaternary register names are the quaternary view on registers, e.g., 8b on 64b systems.
|
|
virtual std::string GetQuaternaryRegisterName(const Reg& reg ATTRIBUTE_UNUSED) {
|
|
UNIMPLEMENTED(FATAL) << "Architecture does not support quaternary registers";
|
|
UNREACHABLE();
|
|
}
|
|
|
|
std::string GetRegisterName(const Reg& reg) {
|
|
return GetRegName<RegisterView::kUsePrimaryName>(reg);
|
|
}
|
|
|
|
protected:
|
|
AssemblerTest() {}
|
|
|
|
void SetUp() override {
|
|
AssemblerTestBase::SetUp();
|
|
allocator_.reset(new ArenaAllocator(&pool_));
|
|
assembler_.reset(CreateAssembler(allocator_.get()));
|
|
SetUpHelpers();
|
|
}
|
|
|
|
void TearDown() override {
|
|
AssemblerTestBase::TearDown();
|
|
assembler_.reset();
|
|
allocator_.reset();
|
|
}
|
|
|
|
// Override this to set up any architecture-specific things, e.g., CPU revision.
|
|
virtual Ass* CreateAssembler(ArenaAllocator* allocator) {
|
|
return new (allocator) Ass(allocator);
|
|
}
|
|
|
|
// Override this to set up any architecture-specific things, e.g., register vectors.
|
|
virtual void SetUpHelpers() {}
|
|
|
|
// Create a couple of immediate values up to the number of bytes given.
|
|
virtual std::vector<int64_t> CreateImmediateValues(size_t imm_bytes, bool as_uint = false) {
|
|
std::vector<int64_t> res;
|
|
res.push_back(0);
|
|
if (!as_uint) {
|
|
res.push_back(-1);
|
|
} else {
|
|
res.push_back(0xFF);
|
|
}
|
|
res.push_back(0x12);
|
|
if (imm_bytes >= 2) {
|
|
res.push_back(0x1234);
|
|
if (!as_uint) {
|
|
res.push_back(-0x1234);
|
|
} else {
|
|
res.push_back(0xFFFF);
|
|
}
|
|
if (imm_bytes >= 4) {
|
|
res.push_back(0x12345678);
|
|
if (!as_uint) {
|
|
res.push_back(-0x12345678);
|
|
} else {
|
|
res.push_back(0xFFFFFFFF);
|
|
}
|
|
if (imm_bytes >= 6) {
|
|
res.push_back(0x123456789ABC);
|
|
if (!as_uint) {
|
|
res.push_back(-0x123456789ABC);
|
|
}
|
|
if (imm_bytes >= 8) {
|
|
res.push_back(0x123456789ABCDEF0);
|
|
if (!as_uint) {
|
|
res.push_back(-0x123456789ABCDEF0);
|
|
} else {
|
|
res.push_back(0xFFFFFFFFFFFFFFFF);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
const int kMaxBitsExhaustiveTest = 8;
|
|
|
|
// Create a couple of immediate values up to the number of bits given.
|
|
virtual std::vector<int64_t> CreateImmediateValuesBits(const int imm_bits, bool as_uint = false) {
|
|
CHECK_GT(imm_bits, 0);
|
|
CHECK_LE(imm_bits, 64);
|
|
std::vector<int64_t> res;
|
|
|
|
if (imm_bits <= kMaxBitsExhaustiveTest) {
|
|
if (as_uint) {
|
|
for (uint64_t i = MinInt<uint64_t>(imm_bits); i <= MaxInt<uint64_t>(imm_bits); i++) {
|
|
res.push_back(static_cast<int64_t>(i));
|
|
}
|
|
} else {
|
|
for (int64_t i = MinInt<int64_t>(imm_bits); i <= MaxInt<int64_t>(imm_bits); i++) {
|
|
res.push_back(i);
|
|
}
|
|
}
|
|
} else {
|
|
if (as_uint) {
|
|
for (uint64_t i = MinInt<uint64_t>(kMaxBitsExhaustiveTest);
|
|
i <= MaxInt<uint64_t>(kMaxBitsExhaustiveTest);
|
|
i++) {
|
|
res.push_back(static_cast<int64_t>(i));
|
|
}
|
|
for (int i = 0; i <= imm_bits; i++) {
|
|
uint64_t j = (MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1) +
|
|
((MaxInt<uint64_t>(imm_bits) -
|
|
(MaxInt<uint64_t>(kMaxBitsExhaustiveTest) + 1))
|
|
* i / imm_bits);
|
|
res.push_back(static_cast<int64_t>(j));
|
|
}
|
|
} else {
|
|
for (int i = 0; i <= imm_bits; i++) {
|
|
int64_t j = MinInt<int64_t>(imm_bits) +
|
|
((((MinInt<int64_t>(kMaxBitsExhaustiveTest) - 1) -
|
|
MinInt<int64_t>(imm_bits))
|
|
* i) / imm_bits);
|
|
res.push_back(static_cast<int64_t>(j));
|
|
}
|
|
for (int64_t i = MinInt<int64_t>(kMaxBitsExhaustiveTest);
|
|
i <= MaxInt<int64_t>(kMaxBitsExhaustiveTest);
|
|
i++) {
|
|
res.push_back(static_cast<int64_t>(i));
|
|
}
|
|
for (int i = 0; i <= imm_bits; i++) {
|
|
int64_t j = (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1) +
|
|
((MaxInt<int64_t>(imm_bits) - (MaxInt<int64_t>(kMaxBitsExhaustiveTest) + 1))
|
|
* i / imm_bits);
|
|
res.push_back(static_cast<int64_t>(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// Create an immediate from the specific value.
|
|
virtual Imm CreateImmediate(int64_t imm_value) = 0;
|
|
|
|
//
|
|
// Addresses repeats.
|
|
//
|
|
|
|
// Repeats over addresses provided by fixture.
|
|
std::string RepeatA(void (Ass::*f)(const Addr&), const std::string& fmt) {
|
|
return RepeatA(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatA(void (Ass::*f)(const Addr&),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedMem<Addr>(f, a, &AssemblerTest::GetAddrName, fmt);
|
|
}
|
|
|
|
// Repeats over addresses and immediates provided by fixture.
|
|
std::string RepeatAI(void (Ass::*f)(const Addr&, const Imm&),
|
|
size_t imm_bytes,
|
|
const std::string& fmt) {
|
|
return RepeatAI(f, imm_bytes, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatAI(void (Ass::*f)(const Addr&, const Imm&),
|
|
size_t imm_bytes,
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedMemImm<Addr>(f, imm_bytes, a, &AssemblerTest::GetAddrName, fmt);
|
|
}
|
|
|
|
// Repeats over registers and addresses provided by fixture.
|
|
std::string RepeatRA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) {
|
|
return RepeatRA(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatRA(void (Ass::*f)(Reg, const Addr&),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedRegMem<Reg, Addr>(
|
|
f,
|
|
GetRegisters(),
|
|
a,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerTest::GetAddrName,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over secondary registers and addresses provided by fixture.
|
|
std::string RepeatrA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) {
|
|
return RepeatrA(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatrA(void (Ass::*f)(Reg, const Addr&),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedRegMem<Reg, Addr>(
|
|
f,
|
|
GetRegisters(),
|
|
a,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
&AssemblerTest::GetAddrName,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over tertiary registers and addresses provided by fixture.
|
|
std::string RepeatwA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) {
|
|
return RepeatwA(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatwA(void (Ass::*f)(Reg, const Addr&),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedRegMem<Reg, Addr>(
|
|
f,
|
|
GetRegisters(),
|
|
a,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>,
|
|
&AssemblerTest::GetAddrName,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over quaternary registers and addresses provided by fixture.
|
|
std::string RepeatbA(void (Ass::*f)(Reg, const Addr&), const std::string& fmt) {
|
|
return RepeatbA(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatbA(void (Ass::*f)(Reg, const Addr&),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedRegMem<Reg, Addr>(
|
|
f,
|
|
GetRegisters(),
|
|
a,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>,
|
|
&AssemblerTest::GetAddrName,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over fp-registers and addresses provided by fixture.
|
|
std::string RepeatFA(void (Ass::*f)(FPReg, const Addr&), const std::string& fmt) {
|
|
return RepeatFA(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatFA(void (Ass::*f)(FPReg, const Addr&),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedRegMem<FPReg, Addr>(
|
|
f,
|
|
GetFPRegisters(),
|
|
a,
|
|
&AssemblerTest::GetFPRegName,
|
|
&AssemblerTest::GetAddrName,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over addresses and registers provided by fixture.
|
|
std::string RepeatAR(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) {
|
|
return RepeatAR(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatAR(void (Ass::*f)(const Addr&, Reg),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedMemReg<Addr, Reg>(
|
|
f,
|
|
a,
|
|
GetRegisters(),
|
|
&AssemblerTest::GetAddrName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over addresses and secondary registers provided by fixture.
|
|
std::string RepeatAr(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) {
|
|
return RepeatAr(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatAr(void (Ass::*f)(const Addr&, Reg),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedMemReg<Addr, Reg>(
|
|
f,
|
|
a,
|
|
GetRegisters(),
|
|
&AssemblerTest::GetAddrName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseSecondaryName>,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over addresses and tertiary registers provided by fixture.
|
|
std::string RepeatAw(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) {
|
|
return RepeatAw(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatAw(void (Ass::*f)(const Addr&, Reg),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedMemReg<Addr, Reg>(
|
|
f,
|
|
a,
|
|
GetRegisters(),
|
|
&AssemblerTest::GetAddrName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseTertiaryName>,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over addresses and quaternary registers provided by fixture.
|
|
std::string RepeatAb(void (Ass::*f)(const Addr&, Reg), const std::string& fmt) {
|
|
return RepeatAb(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatAb(void (Ass::*f)(const Addr&, Reg),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedMemReg<Addr, Reg>(
|
|
f,
|
|
a,
|
|
GetRegisters(),
|
|
&AssemblerTest::GetAddrName,
|
|
&AssemblerTest::GetRegName<RegisterView::kUseQuaternaryName>,
|
|
fmt);
|
|
}
|
|
|
|
// Repeats over addresses and fp-registers provided by fixture.
|
|
std::string RepeatAF(void (Ass::*f)(const Addr&, FPReg), const std::string& fmt) {
|
|
return RepeatAF(f, GetAddresses(), fmt);
|
|
}
|
|
|
|
// Variant that takes explicit vector of addresss
|
|
// (to test restricted addressing modes set).
|
|
std::string RepeatAF(void (Ass::*f)(const Addr&, FPReg),
|
|
const std::vector<Addr>& a,
|
|
const std::string& fmt) {
|
|
return RepeatTemplatedMemReg<Addr, FPReg>(
|
|
f,
|
|
a,
|
|
GetFPRegisters(),
|
|
&AssemblerTest::GetAddrName,
|
|
&AssemblerTest::GetFPRegName,
|
|
fmt);
|
|
}
|
|
|
|
template <typename AddrType>
|
|
std::string RepeatTemplatedMem(void (Ass::*f)(const AddrType&),
|
|
const std::vector<AddrType> addresses,
|
|
std::string (AssemblerTest::*GetAName)(const AddrType&),
|
|
const std::string& fmt) {
|
|
WarnOnCombinations(addresses.size());
|
|
std::string str;
|
|
for (auto addr : addresses) {
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(addr);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string addr_string = (this->*GetAName)(addr);
|
|
size_t addr_index;
|
|
if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
|
|
base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename AddrType>
|
|
std::string RepeatTemplatedMemImm(void (Ass::*f)(const AddrType&, const Imm&),
|
|
size_t imm_bytes,
|
|
const std::vector<AddrType> addresses,
|
|
std::string (AssemblerTest::*GetAName)(const AddrType&),
|
|
const std::string& fmt) {
|
|
std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
|
|
WarnOnCombinations(addresses.size() * imms.size());
|
|
std::string str;
|
|
for (auto addr : addresses) {
|
|
for (int64_t imm : imms) {
|
|
Imm new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(addr, new_imm);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string addr_string = (this->*GetAName)(addr);
|
|
size_t addr_index;
|
|
if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
|
|
base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_string);
|
|
}
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename RegType, typename AddrType>
|
|
std::string RepeatTemplatedRegMem(void (Ass::*f)(RegType, const AddrType&),
|
|
const std::vector<RegType*> registers,
|
|
const std::vector<AddrType> addresses,
|
|
std::string (AssemblerTest::*GetRName)(const RegType&),
|
|
std::string (AssemblerTest::*GetAName)(const AddrType&),
|
|
const std::string& fmt) {
|
|
WarnOnCombinations(addresses.size() * registers.size());
|
|
std::string str;
|
|
for (auto reg : registers) {
|
|
for (auto addr : addresses) {
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg, addr);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg_string = (this->*GetRName)(*reg);
|
|
size_t reg_index;
|
|
if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
|
|
base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
|
|
}
|
|
|
|
std::string addr_string = (this->*GetAName)(addr);
|
|
size_t addr_index;
|
|
if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
|
|
base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename AddrType, typename RegType>
|
|
std::string RepeatTemplatedMemReg(void (Ass::*f)(const AddrType&, RegType),
|
|
const std::vector<AddrType> addresses,
|
|
const std::vector<RegType*> registers,
|
|
std::string (AssemblerTest::*GetAName)(const AddrType&),
|
|
std::string (AssemblerTest::*GetRName)(const RegType&),
|
|
const std::string& fmt) {
|
|
WarnOnCombinations(addresses.size() * registers.size());
|
|
std::string str;
|
|
for (auto addr : addresses) {
|
|
for (auto reg : registers) {
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(addr, *reg);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string addr_string = (this->*GetAName)(addr);
|
|
size_t addr_index;
|
|
if ((addr_index = base.find(ADDRESS_TOKEN)) != std::string::npos) {
|
|
base.replace(addr_index, ConstexprStrLen(ADDRESS_TOKEN), addr_string);
|
|
}
|
|
|
|
std::string reg_string = (this->*GetRName)(*reg);
|
|
size_t reg_index;
|
|
if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
|
|
base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
//
|
|
// Register repeats.
|
|
//
|
|
|
|
template <typename RegType>
|
|
std::string RepeatTemplatedRegister(void (Ass::*f)(RegType),
|
|
const std::vector<RegType*> registers,
|
|
std::string (AssemblerTest::*GetName)(const RegType&),
|
|
const std::string& fmt) {
|
|
std::string str;
|
|
for (auto reg : registers) {
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg_string = (this->*GetName)(*reg);
|
|
size_t reg_index;
|
|
if ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
|
|
base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2),
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
std::string (AssemblerTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerTest::*GetName2)(const Reg2&),
|
|
const std::string& fmt) {
|
|
WarnOnCombinations(reg1_registers.size() * reg2_registers.size());
|
|
|
|
std::string str;
|
|
for (auto reg1 : reg1_registers) {
|
|
for (auto reg2 : reg2_registers) {
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg1, *reg2);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
|
|
base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
|
|
base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedRegistersNoDupes(void (Ass::*f)(Reg1, Reg2),
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
std::string (AssemblerTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerTest::*GetName2)(const Reg2&),
|
|
const std::string& fmt) {
|
|
WarnOnCombinations(reg1_registers.size() * reg2_registers.size());
|
|
|
|
std::string str;
|
|
for (auto reg1 : reg1_registers) {
|
|
for (auto reg2 : reg2_registers) {
|
|
if (reg1 == reg2) continue;
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg1, *reg2);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
|
|
base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
|
|
base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2, typename Reg3>
|
|
std::string RepeatTemplatedRegisters(void (Ass::*f)(Reg1, Reg2, Reg3),
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
const std::vector<Reg3*> reg3_registers,
|
|
std::string (AssemblerTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerTest::*GetName2)(const Reg2&),
|
|
std::string (AssemblerTest::*GetName3)(const Reg3&),
|
|
const std::string& fmt) {
|
|
std::string str;
|
|
for (auto reg1 : reg1_registers) {
|
|
for (auto reg2 : reg2_registers) {
|
|
for (auto reg3 : reg3_registers) {
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg1, *reg2, *reg3);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
|
|
base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
|
|
base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
std::string reg3_string = (this->*GetName3)(*reg3);
|
|
size_t reg3_index;
|
|
while ((reg3_index = base.find(REG3_TOKEN)) != std::string::npos) {
|
|
base.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedRegistersImm(void (Ass::*f)(Reg1, Reg2, const Imm&),
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
std::string (AssemblerTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerTest::*GetName2)(const Reg2&),
|
|
size_t imm_bytes,
|
|
const std::string& fmt) {
|
|
std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
|
|
WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * imms.size());
|
|
|
|
std::string str;
|
|
for (auto reg1 : reg1_registers) {
|
|
for (auto reg2 : reg2_registers) {
|
|
for (int64_t imm : imms) {
|
|
Imm new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg1, *reg2, new_imm);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base.find(REG1_TOKEN)) != std::string::npos) {
|
|
base.replace(reg1_index, ConstexprStrLen(REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base.find(REG2_TOKEN)) != std::string::npos) {
|
|
base.replace(reg2_index, ConstexprStrLen(REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
std::string GetAddrName(const Addr& addr) {
|
|
std::ostringstream saddr;
|
|
saddr << addr;
|
|
return saddr.str();
|
|
}
|
|
|
|
template <RegisterView kRegView>
|
|
std::string GetRegName(const Reg& reg) {
|
|
std::ostringstream sreg;
|
|
switch (kRegView) {
|
|
case RegisterView::kUsePrimaryName:
|
|
sreg << reg;
|
|
break;
|
|
|
|
case RegisterView::kUseSecondaryName:
|
|
sreg << GetSecondaryRegisterName(reg);
|
|
break;
|
|
|
|
case RegisterView::kUseTertiaryName:
|
|
sreg << GetTertiaryRegisterName(reg);
|
|
break;
|
|
|
|
case RegisterView::kUseQuaternaryName:
|
|
sreg << GetQuaternaryRegisterName(reg);
|
|
break;
|
|
}
|
|
return sreg.str();
|
|
}
|
|
|
|
std::string GetFPRegName(const FPReg& reg) {
|
|
std::ostringstream sreg;
|
|
sreg << reg;
|
|
return sreg.str();
|
|
}
|
|
|
|
std::string GetVecRegName(const VecReg& reg) {
|
|
std::ostringstream sreg;
|
|
sreg << reg;
|
|
return sreg.str();
|
|
}
|
|
|
|
void WarnOnCombinations(size_t count) {
|
|
if (count > kWarnManyCombinationsThreshold) {
|
|
GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow.";
|
|
}
|
|
}
|
|
|
|
static constexpr const char* ADDRESS_TOKEN = "{mem}";
|
|
static constexpr const char* REG_TOKEN = "{reg}";
|
|
static constexpr const char* REG1_TOKEN = "{reg1}";
|
|
static constexpr const char* REG2_TOKEN = "{reg2}";
|
|
static constexpr const char* REG3_TOKEN = "{reg3}";
|
|
static constexpr const char* IMM_TOKEN = "{imm}";
|
|
|
|
private:
|
|
template <RegisterView kRegView>
|
|
std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&),
|
|
size_t imm_bytes,
|
|
const std::string& fmt) {
|
|
const std::vector<Reg*> registers = GetRegisters();
|
|
std::string str;
|
|
std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
|
|
|
|
WarnOnCombinations(registers.size() * imms.size());
|
|
|
|
for (auto reg : registers) {
|
|
for (int64_t imm : imms) {
|
|
Imm new_imm = CreateImmediate(imm);
|
|
if (f != nullptr) {
|
|
(assembler_.get()->*f)(*reg, new_imm);
|
|
}
|
|
std::string base = fmt;
|
|
|
|
std::string reg_string = GetRegName<kRegView>(*reg);
|
|
size_t reg_index;
|
|
while ((reg_index = base.find(REG_TOKEN)) != std::string::npos) {
|
|
base.replace(reg_index, ConstexprStrLen(REG_TOKEN), reg_string);
|
|
}
|
|
|
|
size_t imm_index = base.find(IMM_TOKEN);
|
|
if (imm_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << imm;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm_index, ConstexprStrLen(IMM_TOKEN), imm_string);
|
|
}
|
|
|
|
if (str.size() > 0) {
|
|
str += "\n";
|
|
}
|
|
str += base;
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
str += "\n";
|
|
return str;
|
|
}
|
|
|
|
// Override this to pad the code with NOPs to a certain size if needed.
|
|
virtual void Pad(std::vector<uint8_t>& data ATTRIBUTE_UNUSED) {
|
|
}
|
|
|
|
void DriverWrapper(const std::string& assembly_text, const std::string& test_name) {
|
|
assembler_->FinalizeCode();
|
|
size_t cs = assembler_->CodeSize();
|
|
std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>(cs));
|
|
MemoryRegion code(&(*data)[0], data->size());
|
|
assembler_->FinalizeInstructions(code);
|
|
Pad(*data);
|
|
Driver(*data, assembly_text, test_name);
|
|
}
|
|
|
|
static constexpr size_t kWarnManyCombinationsThreshold = 500;
|
|
|
|
MallocArenaPool pool_;
|
|
std::unique_ptr<ArenaAllocator> allocator_;
|
|
std::unique_ptr<Ass> assembler_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AssemblerTest);
|
|
};
|
|
|
|
} // namespace art
|
|
|
|
#endif // ART_COMPILER_UTILS_ASSEMBLER_TEST_H_
|