555 lines
20 KiB
C++
555 lines
20 KiB
C++
/*
|
|
* 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_ARM_ASSEMBLER_ARM_TEST_H_
|
|
#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_
|
|
|
|
#include "utils/assembler_test.h"
|
|
|
|
namespace art {
|
|
|
|
template<typename Ass,
|
|
typename Reg,
|
|
typename FPReg,
|
|
typename Imm,
|
|
typename SOp,
|
|
typename Cond,
|
|
typename SetCc>
|
|
class AssemblerArmTest : public AssemblerTest<Ass, Reg, FPReg, Imm> {
|
|
public:
|
|
typedef AssemblerTest<Ass, Reg, FPReg, Imm> Base;
|
|
|
|
using Base::GetRegisters;
|
|
using Base::GetRegName;
|
|
using Base::CreateImmediate;
|
|
using Base::WarnOnCombinations;
|
|
|
|
static constexpr int64_t kFullImmRangeThreshold = 32;
|
|
|
|
virtual void FillImmediates(std::vector<Imm>& immediates, int64_t imm_min, int64_t imm_max) {
|
|
// Small range: do completely.
|
|
if (imm_max - imm_min <= kFullImmRangeThreshold) {
|
|
for (int64_t i = imm_min; i <= imm_max; ++i) {
|
|
immediates.push_back(CreateImmediate(i));
|
|
}
|
|
} else {
|
|
immediates.push_back(CreateImmediate(imm_min));
|
|
immediates.push_back(CreateImmediate(imm_max));
|
|
if (imm_min < imm_max - 1) {
|
|
immediates.push_back(CreateImmediate(imm_min + 1));
|
|
}
|
|
if (imm_min < imm_max - 2) {
|
|
immediates.push_back(CreateImmediate(imm_min + 2));
|
|
}
|
|
if (imm_min < imm_max - 3) {
|
|
immediates.push_back(CreateImmediate(imm_max - 1));
|
|
}
|
|
if (imm_min < imm_max - 4) {
|
|
immediates.push_back(CreateImmediate((imm_min + imm_max) / 2));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string RepeatRRIIC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond),
|
|
int64_t imm1_min, int64_t imm1_max,
|
|
int64_t imm2_min, int64_t imm2_max,
|
|
std::string fmt) {
|
|
return RepeatTemplatedRRIIC(f, GetRegisters(), GetRegisters(),
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
imm1_min, imm1_max, imm2_min, imm2_max,
|
|
fmt);
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedRRIIC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond),
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
std::string (AssemblerArmTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerArmTest::*GetName2)(const Reg2&),
|
|
int64_t imm1_min, int64_t imm1_max,
|
|
int64_t imm2_min, int64_t imm2_max,
|
|
std::string fmt) {
|
|
std::vector<Imm> immediates1;
|
|
FillImmediates(immediates1, imm1_min, imm1_max);
|
|
std::vector<Imm> immediates2;
|
|
FillImmediates(immediates2, imm2_min, imm2_max);
|
|
|
|
std::vector<Cond>& cond = GetConditions();
|
|
|
|
WarnOnCombinations(cond.size() * immediates1.size() * immediates2.size() *
|
|
reg1_registers.size() * reg2_registers.size());
|
|
|
|
std::ostringstream oss;
|
|
bool first = true;
|
|
for (Cond& c : cond) {
|
|
std::string after_cond = fmt;
|
|
|
|
size_t cond_index = after_cond.find(COND_TOKEN);
|
|
if (cond_index != std::string::npos) {
|
|
after_cond.replace(cond_index, ConstexprStrLen(COND_TOKEN), GetConditionString(c));
|
|
}
|
|
|
|
for (Imm i : immediates1) {
|
|
std::string base = after_cond;
|
|
|
|
size_t imm1_index = base.find(IMM1_TOKEN);
|
|
if (imm1_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << i;
|
|
std::string imm_string = sreg.str();
|
|
base.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string);
|
|
}
|
|
|
|
for (Imm j : immediates2) {
|
|
std::string base2 = base;
|
|
|
|
size_t imm2_index = base2.find(IMM2_TOKEN);
|
|
if (imm2_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << j;
|
|
std::string imm_string = sreg.str();
|
|
base2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string);
|
|
}
|
|
|
|
for (auto reg1 : reg1_registers) {
|
|
std::string base3 = base2;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = base3.find(Base::REG1_TOKEN)) != std::string::npos) {
|
|
base3.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
for (auto reg2 : reg2_registers) {
|
|
std::string base4 = base3;
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = base4.find(Base::REG2_TOKEN)) != std::string::npos) {
|
|
base4.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
oss << "\n";
|
|
}
|
|
oss << base4;
|
|
|
|
(Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
oss << "\n";
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
std::string RepeatRRiiC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond),
|
|
std::vector<std::pair<Imm, Imm>>& immediates,
|
|
std::string fmt) {
|
|
return RepeatTemplatedRRiiC<Reg, Reg>(f, GetRegisters(), GetRegisters(),
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
immediates, fmt);
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedRRiiC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond),
|
|
const std::vector<Reg1*> reg1_registers,
|
|
const std::vector<Reg2*> reg2_registers,
|
|
std::string (AssemblerArmTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerArmTest::*GetName2)(const Reg2&),
|
|
std::vector<std::pair<Imm, Imm>>& immediates,
|
|
std::string fmt) {
|
|
std::vector<Cond>& cond = GetConditions();
|
|
|
|
WarnOnCombinations(cond.size() * immediates.size() * reg1_registers.size() *
|
|
reg2_registers.size());
|
|
|
|
std::ostringstream oss;
|
|
bool first = true;
|
|
for (Cond& c : cond) {
|
|
std::string after_cond = fmt;
|
|
|
|
size_t cond_index = after_cond.find(COND_TOKEN);
|
|
if (cond_index != std::string::npos) {
|
|
after_cond.replace(cond_index, ConstexprStrLen(COND_TOKEN), GetConditionString(c));
|
|
}
|
|
|
|
for (std::pair<Imm, Imm>& pair : immediates) {
|
|
Imm i = pair.first;
|
|
Imm j = pair.second;
|
|
std::string after_imm1 = after_cond;
|
|
|
|
size_t imm1_index = after_imm1.find(IMM1_TOKEN);
|
|
if (imm1_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << i;
|
|
std::string imm_string = sreg.str();
|
|
after_imm1.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string);
|
|
}
|
|
|
|
std::string after_imm2 = after_imm1;
|
|
|
|
size_t imm2_index = after_imm2.find(IMM2_TOKEN);
|
|
if (imm2_index != std::string::npos) {
|
|
std::ostringstream sreg;
|
|
sreg << j;
|
|
std::string imm_string = sreg.str();
|
|
after_imm2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string);
|
|
}
|
|
|
|
for (auto reg1 : reg1_registers) {
|
|
std::string after_reg1 = after_imm2;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
|
|
after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
for (auto reg2 : reg2_registers) {
|
|
std::string after_reg2 = after_reg1;
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
|
|
after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
oss << "\n";
|
|
}
|
|
oss << after_reg2;
|
|
|
|
(Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
oss << "\n";
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
std::string RepeatRRC(void (Ass::*f)(Reg, Reg, Cond), std::string fmt) {
|
|
return RepeatTemplatedRRC(f, GetRegisters(), GetRegisters(), GetConditions(),
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedRRC(void (Ass::*f)(Reg1, Reg2, Cond),
|
|
const std::vector<Reg1*>& reg1_registers,
|
|
const std::vector<Reg2*>& reg2_registers,
|
|
const std::vector<Cond>& cond,
|
|
std::string (AssemblerArmTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerArmTest::*GetName2)(const Reg2&),
|
|
std::string fmt) {
|
|
WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size());
|
|
|
|
std::ostringstream oss;
|
|
bool first = true;
|
|
for (const Cond& c : cond) {
|
|
std::string after_cond = fmt;
|
|
|
|
size_t cond_index = after_cond.find(COND_TOKEN);
|
|
if (cond_index != std::string::npos) {
|
|
after_cond.replace(cond_index, ConstexprStrLen(COND_TOKEN), GetConditionString(c));
|
|
}
|
|
|
|
for (auto reg1 : reg1_registers) {
|
|
std::string after_reg1 = after_cond;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
|
|
after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
for (auto reg2 : reg2_registers) {
|
|
std::string after_reg2 = after_reg1;
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
|
|
after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
oss << "\n";
|
|
}
|
|
oss << after_reg2;
|
|
|
|
(Base::GetAssembler()->*f)(*reg1, *reg2, c);
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
oss << "\n";
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
std::string RepeatRRRC(void (Ass::*f)(Reg, Reg, Reg, Cond), std::string fmt) {
|
|
return RepeatTemplatedRRRC(f, GetRegisters(), GetRegisters(), GetRegisters(), GetConditions(),
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
&AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
|
|
fmt);
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2, typename Reg3>
|
|
std::string RepeatTemplatedRRRC(void (Ass::*f)(Reg1, Reg2, Reg3, Cond),
|
|
const std::vector<Reg1*>& reg1_registers,
|
|
const std::vector<Reg2*>& reg2_registers,
|
|
const std::vector<Reg3*>& reg3_registers,
|
|
const std::vector<Cond>& cond,
|
|
std::string (AssemblerArmTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerArmTest::*GetName2)(const Reg2&),
|
|
std::string (AssemblerArmTest::*GetName3)(const Reg3&),
|
|
std::string fmt) {
|
|
WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() *
|
|
reg3_registers.size());
|
|
|
|
std::ostringstream oss;
|
|
bool first = true;
|
|
for (const Cond& c : cond) {
|
|
std::string after_cond = fmt;
|
|
|
|
size_t cond_index = after_cond.find(COND_TOKEN);
|
|
if (cond_index != std::string::npos) {
|
|
after_cond.replace(cond_index, ConstexprStrLen(COND_TOKEN), GetConditionString(c));
|
|
}
|
|
|
|
for (auto reg1 : reg1_registers) {
|
|
std::string after_reg1 = after_cond;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
|
|
after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
for (auto reg2 : reg2_registers) {
|
|
std::string after_reg2 = after_reg1;
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
|
|
after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
for (auto reg3 : reg3_registers) {
|
|
std::string after_reg3 = after_reg2;
|
|
|
|
std::string reg3_string = (this->*GetName3)(*reg3);
|
|
size_t reg3_index;
|
|
while ((reg3_index = after_reg3.find(REG3_TOKEN)) != std::string::npos) {
|
|
after_reg3.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
|
|
}
|
|
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
oss << "\n";
|
|
}
|
|
oss << after_reg3;
|
|
|
|
(Base::GetAssembler()->*f)(*reg1, *reg2, *reg3, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
oss << "\n";
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
template <typename RegT>
|
|
std::string RepeatTemplatedRSC(void (Ass::*f)(RegT, SOp, Cond),
|
|
const std::vector<RegT*>& registers,
|
|
const std::vector<SOp>& shifts,
|
|
const std::vector<Cond>& cond,
|
|
std::string (AssemblerArmTest::*GetName)(const RegT&),
|
|
std::string fmt) {
|
|
WarnOnCombinations(cond.size() * registers.size() * shifts.size());
|
|
|
|
std::ostringstream oss;
|
|
bool first = true;
|
|
for (const Cond& c : cond) {
|
|
std::string after_cond = fmt;
|
|
|
|
size_t cond_index = after_cond.find(COND_TOKEN);
|
|
if (cond_index != std::string::npos) {
|
|
after_cond.replace(cond_index, ConstexprStrLen(COND_TOKEN), GetConditionString(c));
|
|
}
|
|
|
|
for (const SOp& shift : shifts) {
|
|
std::string after_shift = after_cond;
|
|
|
|
std::string shift_string = GetShiftString(shift);
|
|
size_t shift_index;
|
|
while ((shift_index = after_shift.find(Base::SHIFT_TOKEN)) != std::string::npos) {
|
|
after_shift.replace(shift_index, ConstexprStrLen(Base::SHIFT_TOKEN), shift_string);
|
|
}
|
|
|
|
for (auto reg : registers) {
|
|
std::string after_reg = after_shift;
|
|
|
|
std::string reg_string = (this->*GetName)(*reg);
|
|
size_t reg_index;
|
|
while ((reg_index = after_reg.find(Base::REG_TOKEN)) != std::string::npos) {
|
|
after_reg.replace(reg_index, ConstexprStrLen(Base::REG_TOKEN), reg_string);
|
|
}
|
|
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
oss << "\n";
|
|
}
|
|
oss << after_reg;
|
|
|
|
(Base::GetAssembler()->*f)(*reg, shift, c);
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
oss << "\n";
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
template <typename Reg1, typename Reg2>
|
|
std::string RepeatTemplatedRRSC(void (Ass::*f)(Reg1, Reg2, const SOp&, Cond),
|
|
const std::vector<Reg1*>& reg1_registers,
|
|
const std::vector<Reg2*>& reg2_registers,
|
|
const std::vector<SOp>& shifts,
|
|
const std::vector<Cond>& cond,
|
|
std::string (AssemblerArmTest::*GetName1)(const Reg1&),
|
|
std::string (AssemblerArmTest::*GetName2)(const Reg2&),
|
|
std::string fmt) {
|
|
WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() * shifts.size());
|
|
|
|
std::ostringstream oss;
|
|
bool first = true;
|
|
for (const Cond& c : cond) {
|
|
std::string after_cond = fmt;
|
|
|
|
size_t cond_index = after_cond.find(COND_TOKEN);
|
|
if (cond_index != std::string::npos) {
|
|
after_cond.replace(cond_index, ConstexprStrLen(COND_TOKEN), GetConditionString(c));
|
|
}
|
|
|
|
for (const SOp& shift : shifts) {
|
|
std::string after_shift = after_cond;
|
|
|
|
std::string shift_string = GetShiftString(shift);
|
|
size_t shift_index;
|
|
while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) {
|
|
after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
|
|
}
|
|
|
|
for (auto reg1 : reg1_registers) {
|
|
std::string after_reg1 = after_shift;
|
|
|
|
std::string reg1_string = (this->*GetName1)(*reg1);
|
|
size_t reg1_index;
|
|
while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
|
|
after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
|
|
}
|
|
|
|
for (auto reg2 : reg2_registers) {
|
|
std::string after_reg2 = after_reg1;
|
|
|
|
std::string reg2_string = (this->*GetName2)(*reg2);
|
|
size_t reg2_index;
|
|
while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
|
|
after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
|
|
}
|
|
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
oss << "\n";
|
|
}
|
|
oss << after_reg2;
|
|
|
|
(Base::GetAssembler()->*f)(*reg1, *reg2, shift, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Add a newline at the end.
|
|
oss << "\n";
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
protected:
|
|
explicit AssemblerArmTest() {}
|
|
|
|
virtual std::vector<Cond>& GetConditions() = 0;
|
|
virtual std::string GetConditionString(Cond c) = 0;
|
|
|
|
virtual std::vector<SetCc>& GetSetCcs() = 0;
|
|
virtual std::string GetSetCcString(SetCc s) = 0;
|
|
|
|
virtual std::vector<SOp>& GetShiftOperands() = 0;
|
|
virtual std::string GetShiftString(SOp sop) = 0;
|
|
|
|
virtual Reg GetPCRegister() = 0;
|
|
virtual std::vector<Reg*> GetRegistersWithoutPC() {
|
|
std::vector<Reg*> without_pc = GetRegisters();
|
|
Reg pc_reg = GetPCRegister();
|
|
|
|
for (auto it = without_pc.begin(); it != without_pc.end(); ++it) {
|
|
if (**it == pc_reg) {
|
|
without_pc.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return without_pc;
|
|
}
|
|
|
|
static constexpr const char* IMM1_TOKEN = "{imm1}";
|
|
static constexpr const char* IMM2_TOKEN = "{imm2}";
|
|
static constexpr const char* REG3_TOKEN = "{reg3}";
|
|
static constexpr const char* REG4_TOKEN = "{reg4}";
|
|
static constexpr const char* COND_TOKEN = "{cond}";
|
|
static constexpr const char* SET_CC_TOKEN = "{s}";
|
|
static constexpr const char* SHIFT_TOKEN = "{shift}";
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(AssemblerArmTest);
|
|
};
|
|
|
|
} // namespace art
|
|
|
|
#endif // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_
|