#include"base/macros.h" #include"base/os.h" #include"base/utils.h" #include"common_runtime_test.h"// For ScratchDir. #include"elf/elf_builder.h" #include"elf/elf_debug_reader.h" #include"exec_utils.h" #include"stream/file_output_stream.h"
namespace art HIDDEN {
// If you want to take a look at the differences between the ART assembler and clang, // set this flag to true. The disassembled files will then remain in the tmp directory. static constexpr bool kKeepDisassembledFiles = false;
// We put this into a class as gtests are self-contained, so this helper needs to be in an h-file. class AssemblerTestBase : public ::testing::Test { public:
AssemblerTestBase() {}
void SetUp() override { // Fake a runtime test for ScratchDir.
CommonArtTest::SetUpAndroidRootEnvVars();
CommonRuntimeTest::SetUpAndroidDataDir(android_data_);
scratch_dir_.emplace(/*keep_files=*/ kKeepDisassembledFiles);
}
void TearDown() override { // We leave temporaries in case this failed so we can debug issues.
CommonRuntimeTest::TearDownAndroidDataDir(android_data_, false);
}
// This is intended to be run as a test. bool CheckTools() { for (const std::string& cmd : { GetAssemblerCommand()[0], GetDisassemblerCommand()[0] }) { if (!OS::FileExists(cmd.c_str())) {
LOG(ERROR) << "Could not find " << cmd; returnfalse;
}
} returntrue;
}
// Driver() assembles and compares the results. If the results are not equal and we have a // disassembler, disassemble both and check whether they have the same mnemonics (in which case // we just warn). void Driver(const std::vector<uint8_t>& art_code, const std::string& assembly_text, const std::string& test_name) {
ASSERT_NE(assembly_text.length(), 0U) << "Empty assembly";
InstructionSet isa = GetIsa(); auto test_path = [&](constchar* ext) { return scratch_dir_->GetPath() + test_name + ext; };
// Read the code produced by assembler from the ELF file.
std::vector<uint8_t> ref_code; if (Is64BitInstructionSet(isa)) {
ReadElf</*IsElf64=*/true>(ref_obj_file, &ref_code);
} else {
ReadElf</*IsElf64=*/false>(ref_obj_file, &ref_code);
}
// Compare the ART generated code to the expected reference code. if (art_code == ref_code) { return; // Success!
}
// Create ELF file containing the ART code.
std::string art_obj_file = test_path(".art.o"); if (Is64BitInstructionSet(isa)) {
WriteElf</*IsElf64=*/true>(art_obj_file, isa, art_code);
} else {
WriteElf</*IsElf64=*/false>(art_obj_file, isa, art_code);
}
// Disassemble both object files, and check that the outputs match.
std::string art_disassembly;
ASSERT_TRUE(Disassemble(art_obj_file, &art_disassembly));
art_disassembly = Replace(art_disassembly, art_obj_file, test_path("<extension-redacted>"));
art_disassembly = StripComments(art_disassembly);
std::string ref_disassembly;
ASSERT_TRUE(Disassemble(ref_obj_file, &ref_disassembly));
ref_disassembly = Replace(ref_disassembly, ref_obj_file, test_path("<extension-redacted>"));
ref_disassembly = StripComments(ref_disassembly);
ASSERT_EQ(art_disassembly, ref_disassembly) << "Outputs (and disassembly) not identical.";
// ART produced different (but valid) code than the reference assembler, report it. if (art_code.size() > ref_code.size()) {
ADD_FAILURE() << "ART code is larger then the reference code, but the disassembly" "of machine code is equal: this means that ART is generating sub-optimal encoding! " "ART code size=" << art_code.size() << ", reference code size=" << ref_code.size();
} elseif (art_code.size() < ref_code.size()) {
ADD_FAILURE() << "ART code is smaller than the reference code. Too good to be true?";
} elseif (require_same_encoding_) {
ADD_FAILURE() << "Reference assembler chose a different encoding than ART (of the same size)" " but the test is set up to require the same encoding";
} else {
LOG(INFO) << "Reference assembler chose a different encoding than ART (of the same size)";
}
}
virtual std::vector<std::string> GetAssemblerCommand() {
InstructionSet isa = GetIsa(); switch (isa) { case InstructionSet::kRiscv64: // TODO(riscv64): Support compression (RV32C) in assembler and tests (add `c` to `-march=`). return {FindTool("clang"), "--compile", "-target", "riscv64-linux-gnu", "-march=rv64imafdcv_zba_zbb_zbs_zca_zcd_zcb", // Force the assembler to fully emit branch instructions instead of leaving // offsets unresolved with relocation information for the linker. "-mno-relax"}; case InstructionSet::kX86: return {FindTool("clang"), "--compile", "-target", "i386-linux-gnu"}; case InstructionSet::kX86_64: return {FindTool("clang"), "--compile", "-target", "x86_64-linux-gnu"}; default:
LOG(FATAL) << "Unknown instruction set: " << isa;
UNREACHABLE();
}
}
virtual std::vector<std::string> GetDisassemblerCommand() { switch (GetIsa()) { case InstructionSet::kThumb2: return {FindTool("llvm-objdump"), "--disassemble", "--no-print-imm-hex", "--triple", "thumbv7a-linux-gnueabi"}; case InstructionSet::kRiscv64: return {FindTool("llvm-objdump"), "--disassemble", "--no-print-imm-hex", "--no-show-raw-insn", // Disassemble Standard Extensions supported by the assembler. "--mattr=+F,+D,+A,+C,+V,+Zba,+Zbb,+Zca,+Zcd,+Zcb", "-M", "no-aliases"}; default: return {
FindTool("llvm-objdump"), "--disassemble", "--no-print-imm-hex", "--no-show-raw-insn"};
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.