| /* |
| * Copyright (C) 2015 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. |
| */ |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "arch/instruction_set.h" |
| #include "base/macros.h" |
| #include "base/runtime_debug.h" |
| #include "cfi_test.h" |
| #include "driver/compiler_options.h" |
| #include "gtest/gtest.h" |
| #include "optimizing/code_generator.h" |
| #include "optimizing/optimizing_unit_test.h" |
| #include "read_barrier_config.h" |
| #include "utils/arm/assembler_arm_vixl.h" |
| #include "utils/assembler.h" |
| |
| #include "optimizing/optimizing_cfi_test_expected.inc" |
| |
| namespace vixl32 = vixl::aarch32; |
| |
| namespace art HIDDEN { |
| |
| // Run the tests only on host. |
| #ifndef ART_TARGET_ANDROID |
| |
| class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { |
| public: |
| // Enable this flag to generate the expected outputs. |
| static constexpr bool kGenerateExpected = false; |
| |
| OptimizingCFITest() |
| : graph_(nullptr), |
| code_gen_(), |
| blocks_(GetAllocator()->Adapter()) {} |
| |
| void SetUpFrame(InstructionSet isa) { |
| OverrideInstructionSetFeatures(isa, "default"); |
| |
| // Ensure that slow-debug is off, so that there is no unexpected read-barrier check emitted. |
| SetRuntimeDebugFlagsEnabled(false); |
| |
| // Setup simple context. |
| graph_ = CreateGraph(); |
| // Generate simple frame with some spills. |
| code_gen_ = CodeGenerator::Create(graph_, *compiler_options_); |
| code_gen_->GetAssembler()->cfi().SetEnabled(true); |
| code_gen_->InitializeCodeGenerationData(); |
| const int frame_size = 64; |
| int core_reg = 0; |
| int fp_reg = 0; |
| for (int i = 0; i < 2; i++) { // Two registers of each kind. |
| for (; core_reg < 32; core_reg++) { |
| if (code_gen_->IsCoreCalleeSaveRegister(core_reg)) { |
| auto location = Location::RegisterLocation(core_reg); |
| code_gen_->AddAllocatedRegister(location); |
| core_reg++; |
| break; |
| } |
| } |
| for (; fp_reg < 32; fp_reg++) { |
| if (code_gen_->IsFloatingPointCalleeSaveRegister(fp_reg)) { |
| auto location = Location::FpuRegisterLocation(fp_reg); |
| code_gen_->AddAllocatedRegister(location); |
| fp_reg++; |
| break; |
| } |
| } |
| } |
| code_gen_->block_order_ = &blocks_; |
| code_gen_->ComputeSpillMask(); |
| code_gen_->SetFrameSize(frame_size); |
| code_gen_->GenerateFrameEntry(); |
| } |
| |
| void Finish() { |
| code_gen_->GenerateFrameExit(); |
| code_gen_->Finalize(&code_allocator_); |
| } |
| |
| void Check(InstructionSet isa, |
| const char* isa_str, |
| const std::vector<uint8_t>& expected_asm, |
| const std::vector<uint8_t>& expected_cfi) { |
| // Get the outputs. |
| ArrayRef<const uint8_t> actual_asm = code_allocator_.GetMemory(); |
| Assembler* opt_asm = code_gen_->GetAssembler(); |
| ArrayRef<const uint8_t> actual_cfi(*(opt_asm->cfi().data())); |
| |
| if (kGenerateExpected) { |
| GenerateExpected(stdout, isa, isa_str, actual_asm, actual_cfi); |
| } else { |
| EXPECT_EQ(ArrayRef<const uint8_t>(expected_asm), actual_asm); |
| EXPECT_EQ(ArrayRef<const uint8_t>(expected_cfi), actual_cfi); |
| } |
| } |
| |
| void TestImpl(InstructionSet isa, const char* |
| isa_str, |
| const std::vector<uint8_t>& expected_asm, |
| const std::vector<uint8_t>& expected_cfi) { |
| SetUpFrame(isa); |
| Finish(); |
| Check(isa, isa_str, expected_asm, expected_cfi); |
| } |
| |
| CodeGenerator* GetCodeGenerator() { |
| return code_gen_.get(); |
| } |
| |
| private: |
| class InternalCodeAllocator : public CodeAllocator { |
| public: |
| InternalCodeAllocator() {} |
| |
| uint8_t* Allocate(size_t size) override { |
| memory_.resize(size); |
| return memory_.data(); |
| } |
| |
| ArrayRef<const uint8_t> GetMemory() const override { return ArrayRef<const uint8_t>(memory_); } |
| |
| private: |
| std::vector<uint8_t> memory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator); |
| }; |
| |
| HGraph* graph_; |
| std::unique_ptr<CodeGenerator> code_gen_; |
| ArenaVector<HBasicBlock*> blocks_; |
| InternalCodeAllocator code_allocator_; |
| }; |
| |
| #define TEST_ISA(isa) \ |
| TEST_F(OptimizingCFITest, isa) { \ |
| std::vector<uint8_t> expected_asm( \ |
| expected_asm_##isa, \ |
| expected_asm_##isa + arraysize(expected_asm_##isa)); \ |
| std::vector<uint8_t> expected_cfi( \ |
| expected_cfi_##isa, \ |
| expected_cfi_##isa + arraysize(expected_cfi_##isa)); \ |
| TestImpl(InstructionSet::isa, #isa, expected_asm, expected_cfi); \ |
| } |
| |
| #ifdef ART_ENABLE_CODEGEN_arm |
| TEST_ISA(kThumb2) |
| #endif |
| |
| #ifdef ART_ENABLE_CODEGEN_arm64 |
| // Run the tests for ARM64 only with Baker read barriers, as the |
| // expected generated code saves and restore X21 and X22 (instead of |
| // X20 and X21), as X20 is used as Marking Register in the Baker read |
| // barrier configuration, and as such is removed from the set of |
| // callee-save registers in the ARM64 code generator of the Optimizing |
| // compiler. |
| // |
| // We can't use compile-time macros for read-barrier as the introduction |
| // of userfaultfd-GC has made it a runtime choice. |
| TEST_F(OptimizingCFITest, kArm64) { |
| if (kUseBakerReadBarrier && gUseReadBarrier) { |
| std::vector<uint8_t> expected_asm( |
| expected_asm_kArm64, |
| expected_asm_kArm64 + arraysize(expected_asm_kArm64)); |
| std::vector<uint8_t> expected_cfi( |
| expected_cfi_kArm64, |
| expected_cfi_kArm64 + arraysize(expected_cfi_kArm64)); |
| TestImpl(InstructionSet::kArm64, "kArm64", expected_asm, expected_cfi); |
| } |
| } |
| #endif |
| |
| #ifdef ART_ENABLE_CODEGEN_x86 |
| TEST_ISA(kX86) |
| #endif |
| |
| #ifdef ART_ENABLE_CODEGEN_x86_64 |
| TEST_ISA(kX86_64) |
| #endif |
| |
| #ifdef ART_ENABLE_CODEGEN_arm |
| TEST_F(OptimizingCFITest, kThumb2Adjust) { |
| using vixl32::r0; |
| std::vector<uint8_t> expected_asm( |
| expected_asm_kThumb2_adjust, |
| expected_asm_kThumb2_adjust + arraysize(expected_asm_kThumb2_adjust)); |
| std::vector<uint8_t> expected_cfi( |
| expected_cfi_kThumb2_adjust, |
| expected_cfi_kThumb2_adjust + arraysize(expected_cfi_kThumb2_adjust)); |
| SetUpFrame(InstructionSet::kThumb2); |
| #define __ down_cast<arm::ArmVIXLAssembler*>(GetCodeGenerator() \ |
| ->GetAssembler())->GetVIXLAssembler()-> |
| vixl32::Label target; |
| __ CompareAndBranchIfZero(r0, &target); |
| // Push the target out of range of CBZ. |
| for (size_t i = 0; i != 65; ++i) { |
| __ Ldr(r0, vixl32::MemOperand(r0)); |
| } |
| __ Bind(&target); |
| #undef __ |
| Finish(); |
| Check(InstructionSet::kThumb2, "kThumb2_adjust", expected_asm, expected_cfi); |
| } |
| #endif |
| |
| #endif // ART_TARGET_ANDROID |
| |
| } // namespace art |