// Return all combinations of ISA and code generator that are executable on // hardware, or on simulator, and that we'd like to test. static ::std::vector<CodegenTargetConfig> GetTargetConfigs() {
::std::vector<CodegenTargetConfig> v;
::std::vector<CodegenTargetConfig> test_config_candidates = { #ifdef ART_ENABLE_CODEGEN_arm // TODO: Should't this be `kThumb2` instead of `kArm` here?
CodegenTargetConfig(InstructionSet::kArm, create_codegen_arm_vixl32), #endif #ifdef ART_ENABLE_CODEGEN_arm64
CodegenTargetConfig(InstructionSet::kArm64, create_codegen_arm64), #endif #ifdef ART_ENABLE_CODEGEN_x86
CodegenTargetConfig(InstructionSet::kX86, create_codegen_x86), #endif #ifdef ART_ENABLE_CODEGEN_x86_64
CodegenTargetConfig(InstructionSet::kX86_64, create_codegen_x86_64), #endif
};
for (const CodegenTargetConfig& test_config : test_config_candidates) { if (CanExecuteISA(test_config.GetInstructionSet())) {
v.push_back(test_config);
}
}
TEST_F(CodegenTest, MaterializedCondition1) { for (CodegenTargetConfig target_config : GetTargetConfigs()) { // Check that condition are materialized correctly. A materialized condition // should yield `1` if it evaluated to true, and `0` otherwise. // We force the materialization of comparisons for different combinations of
// inputs and check the results.
int lhs[] = {1, 2, -1, 2, 0xabc}; int rhs[] = {2, 1, 2, -1, 0xabc};
for (size_t i = 0; i < arraysize(lhs); i++) {
HBasicBlock* code_block = InitEntryMainExitGraph();
TEST_F(CodegenTest, MaterializedCondition2) { for (CodegenTargetConfig target_config : GetTargetConfigs()) { // Check that HIf correctly interprets a materialized condition. // We force the materialization of comparisons for different combinations of // inputs. An HIf takes the materialized combination as input and returns a // value that we verify.
int lhs[] = {1, 2, -1, 2, 0xabc}; int rhs[] = {2, 1, 2, -1, 0xabc};
for (size_t i = 0; i < arraysize(lhs); i++) {
HGraph* graph = CreateGraph();
HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
HInstruction* cmp_lt = MakeCondition(if_block, kCondLT, cst_lhs, cst_rhs); // We insert a fake instruction to separate the HIf from the HLessThan // and force the materialization of the condition.
HInstruction* force_materialization = new (GetAllocator()) HMemoryBarrier(MemBarrierKind::kAnyAny, 0);
if_block->AddInstruction(force_materialization);
MakeIf(if_block, cmp_lt);
TEST_F(CodegenTest, ComparisonsInt) { for (CodegenTargetConfig target_config : GetTargetConfigs()) { for (int64_t i = -1; i <= 1; i++) { for (int64_t j = -1; j <= 1; j++) { for (int cond = kCondFirst; cond <= kCondLast; cond++) {
TestComparison( static_cast<IfCondition>(cond), i, j, DataType::Type::kInt32, target_config);
}
}
}
}
}
TEST_F(CodegenTest, ComparisonsLong) { for (CodegenTargetConfig target_config : GetTargetConfigs()) { for (int64_t i = -1; i <= 1; i++) { for (int64_t j = -1; j <= 1; j++) { for (int cond = kCondFirst; cond <= kCondLast; cond++) {
TestComparison( static_cast<IfCondition>(cond), i, j, DataType::Type::kInt64, target_config);
}
}
}
}
}
// Tests a PackedSwitch in a very large HGraph; validates that the switch jump table is in // range for the PC-relative load in the codegen visitor. void CodegenTest::TestPackedSwitch(const CodegenTargetConfig target_config) {
HBasicBlock* return_block = InitEntryMainExitGraph();
constexpr DataType::Type data_type = DataType::Type::kInt32;
// A number of entries - we are interested to test jump table implementation.
constexpr size_t kNumSwitchEntries = 10;
// Number of jump targets (including a 'default' case).
constexpr size_t kNumBB = kNumSwitchEntries + 1; // Some arbitrary value to be used as input.
constexpr int kInputValue = kNumBB - 4;
// Emit a huge number of HAdds - to simulate a very large HGraph.
constexpr int kNumOfAdds = 2 * 1024 * 1024; for (int i = 0; i < kNumOfAdds; i++) {
return_val = MakeBinOp<HAdd>(return_block, data_type, return_val, constant_1);
}
TEST_F(CodegenTest, DeoptimizeSlowPathDeduplication) { for (CodegenTargetConfig target_config : GetTargetConfigs()) {
SCOPED_TRACE(target_config.GetInstructionSet());
// We intentionally do not wrap test cases with ASSERT_NO_FATAL_FAILURE to allow execution // to continue if one test case hits an assertion failure. This is fine since the test cases // are independent.
// Make `param1` and `param2` live for both deoptimize instructions. This prevents `neg` // from reusing the `param1` and `param2` registers so that the deoptimize instructions // have different sets of live registers.
MakeBinOp<HAdd>(block, DataType::Type::kInt32, param1, param2);
// Make `neg` live for `deopt_live_neg`. Since `cond` is materialized and occupies the // register for the first deoptimize entrypoint parameter, `neg` uses another register // that is not spilled on the slow path.
MakeReturn(block, neg);
TestDeoptimizeSlowPathDeduplication( "Different sets of spilled registers",
target_config,
[this](HBasicBlock* block, HInstruction* param1, HInstruction* param2) {
HCondition* cond = MakeCondition(block, kCondA, param1, param2);
HDeoptimize* deopt_live_method =
MakeDeoptimize(block, cond, DeoptimizationKind::kLoopNullBCE); // Make the current method live. It is located in the same register as the first parameter // of the deoptimize entrypoint.
HCurrentMethod* method = block->GetGraph()->GetCurrentMethod();
MakeUnOp<HNeg>(block, method->GetType(), method);
HDeoptimize* deopt = MakeDeoptimize(block, cond, DeoptimizationKind::kLoopNullBCE);
// This will result in calling EmitSwap -> void ParallelMoveResolverARMVIXL::Exchange(int mem1, // int mem2) which was faulty (before the fix). So previously GPR and FP scratch registers were // used as temps; however GPR scratch register is required for big stack offsets which don't fit // LDR encoding. So the following code is a regression test for that situation.
HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
move->AddMove(Location::StackSlot(0), Location::StackSlot(8192), DataType::Type::kInt32, nullptr);
move->AddMove(Location::StackSlot(8192), Location::StackSlot(0), DataType::Type::kInt32, nullptr);
codegen.GetMoveResolver()->EmitNativeCode(move);
// The following ParallelMove used to fail this assertion: // // Assertion failed (!available->IsEmpty()) // // in vixl::aarch64::UseScratchRegisterScope::AcquireNextAvailable, // because of the following situation: // // 1. a temp register (IP0) is allocated as a scratch register by // the parallel move resolver to solve a cycle (swap): // // [ source=DS0 destination=DS257 type=PrimDouble instruction=null ] // [ source=DS257 destination=DS0 type=PrimDouble instruction=null ] // // 2. within CodeGeneratorARM64::MoveLocation, another temp // register (IP1) is allocated to generate the swap between two // double stack slots; // // 3. VIXL requires a third temp register to emit the `Ldr` or // `Str` operation from CodeGeneratorARM64::MoveLocation (as // one of the stack slots' offsets cannot be encoded as an // immediate), but the pool of (core) temp registers is now // empty. // // The solution used so far is to use a floating-point temp register // (D31) in step #2, so that IP1 is available for step #3.
// Check that ParallelMoveResolver works fine for ARM64 for both cases when SIMD is on and off.
TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) {
std::unique_ptr<CompilerOptions> compiler_options =
CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
HGraph* graph = CreateGraph();
arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
codegen.Initialize();
graph->SetHasTraditionalSIMD(true); for (int i = 0; i < 2; i++) {
HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
move->AddMove(Location::SIMDStackSlot(0),
Location::SIMDStackSlot(257),
DataType::Type::kFloat64,
nullptr);
move->AddMove(Location::SIMDStackSlot(257),
Location::SIMDStackSlot(0),
DataType::Type::kFloat64,
nullptr);
move->AddMove(Location::FpuRegister(0),
Location::FpuRegister(1),
DataType::Type::kFloat64,
nullptr);
move->AddMove(Location::FpuRegister(1),
Location::FpuRegister(0),
DataType::Type::kFloat64,
nullptr);
codegen.GetMoveResolver()->EmitNativeCode(move);
graph->SetHasTraditionalSIMD(false);
}
codegen.Finalize();
}
// Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a75 as example).
TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA75) {
std::unique_ptr<CompilerOptions> compiler_options =
CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "cortex-a75");
HGraph* graph = CreateGraph();
arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
// Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a53 as example).
TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA53) {
std::unique_ptr<CompilerOptions> compiler_options =
CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "cortex-a53");
HGraph* graph = CreateGraph();
arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
// The following two tests check that for both SIMD and non-SIMD graphs exactly 64-bit is // allocated on stack per callee-saved FP register to be preserved in the frame entry as // ABI states.
TEST_F(CodegenTest, ARM64FrameSizeSIMD) {
std::unique_ptr<CompilerOptions> compiler_options =
CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
HGraph* graph = CreateGraph();
arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
// This test checks that the result of the VecPredToBoolean instruction doesn't depend on // conditional flags that can be updated by other instructions. For example: // // VecPredWhile p0, opa, opb // Below opb, opa // VecPredToBoolean p0 // // where Below updates conditions flags after VecPredWhile.
TEST_F(CodegenTest, ARM64SvePredicateToBoolean) {
std::unique_ptr<CompilerOptions> compiler_options =
CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default", "sve"); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) {
HBasicBlock* block = InitEntryMainExitGraph();
TestCodeGeneratorARM64 codegen(graph_, *compiler_options); if (!codegen.SupportsPredicatedSIMD()) {
GTEST_SKIP() << "Predicated SIMD is not supported.";
}
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.