| // Copyright 2013 Google Inc. All Rights Reserved. |
| // |
| // 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. |
| // |
| // Unittests for liveness analysis. |
| |
| #include "syzygy/block_graph/analysis/liveness_analysis.h" |
| |
| #include "base/bind.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "syzygy/block_graph/basic_block_assembler.h" |
| #include "syzygy/block_graph/analysis/liveness_analysis_internal.h" |
| |
| #include "mnemonics.h" // NOLINT |
| |
| namespace block_graph { |
| namespace analysis { |
| |
| namespace { |
| |
| typedef BasicBlockSubGraph::BBCollection BBCollection; |
| typedef block_graph::analysis::LivenessAnalysis::State State; |
| typedef block_graph::analysis::LivenessAnalysis::StateHelper StateHelper; |
| typedef block_graph::BasicBlockSubGraph::BasicBlock BasicBlock; |
| typedef block_graph::BasicBlockSubGraph::BasicBlock::Instructions Instructions; |
| typedef block_graph::BasicBlockSubGraph::BasicBlock::Successors Successors; |
| typedef block_graph::BasicBlockSubGraph::BasicCodeBlock BasicCodeBlock; |
| typedef block_graph::BasicBlockSubGraph::BlockDescription BlockDescription; |
| |
| // _asm mov eax, 0 |
| const uint8_t kMovEaxZero[5] = {0xB8, 0x00, 0x00, 0x00, 0x00}; |
| // _asm mov ebx, 0 |
| const uint8_t kMovEbxZero[5] = {0xBB, 0x00, 0x00, 0x00, 0x00}; |
| // _asm mov ecx, 0 |
| const uint8_t kMovEcxZero[5] = {0xB9, 0x00, 0x00, 0x00, 0x00}; |
| // _asm mov edx, 0 |
| const uint8_t kMovEdxZero[5] = {0xBA, 0x00, 0x00, 0x00, 0x00}; |
| // _asm mov esi, 0 |
| const uint8_t kMovEsiZero[5] = {0xBE, 0x00, 0x00, 0x00, 0x00}; |
| // _asm mov edi, 0 |
| const uint8_t kMovEdiZero[5] = {0xBF, 0x00, 0x00, 0x00, 0x00}; |
| // _asm mov esp, 0 |
| const uint8_t kMovEspZero[5] = {0xBC, 0x00, 0x00, 0x00, 0x00}; |
| // _asm mov ebp, 0 |
| const uint8_t kMovEbpZero[5] = {0xBD, 0x00, 0x00, 0x00, 0x00}; |
| // _asm cmp eax, ebx |
| const uint8_t kCmpEaxEbx[2] = {0x3B, 0xC3}; |
| // _asm mov ax, 0 |
| const uint8_t kMovAxZero[4] = {0x66, 0xB8, 0x00, 0x00}; |
| // _asm mov al, 0 |
| const uint8_t kMovAlZero[2] = {0xB0, 0x00}; |
| |
| class LivenessAnalysisTest : public testing::Test { |
| public: |
| LivenessAnalysisTest(); |
| |
| inline bool is_def(const assm::Register& reg) const { |
| return defs_.IsLive(reg); |
| } |
| |
| inline bool is_use(const assm::Register& reg) const { |
| return uses_.IsLive(reg); |
| } |
| |
| inline bool is_live(const assm::Register& reg) const { |
| return state_.IsLive(reg); |
| } |
| |
| inline bool are_arithmetic_flags_live() const { |
| return state_.AreArithmeticFlagsLive(); |
| } |
| |
| template <size_t N> |
| void AddInstructionFromBuffer(const uint8_t(&data)[N]); |
| void DefineAllRegisters(); |
| void AnalyzeInstructionsWithoutReset(); |
| void AnalyzeInstructions(); |
| |
| template <size_t N> |
| void UpdateDefsUsesFromBuffer(const uint8_t(&data)[N]); |
| |
| template <size_t N> |
| void AnalyzeSingleInstructionFromBuffer(const uint8_t(&data)[N]); |
| |
| bool CheckCarryFlagInstruction(bool expect_on, bool expect_off); |
| |
| void AddSuccessorBetween(Successor::Condition condition, |
| BasicCodeBlock* from, |
| BasicCodeBlock* to); |
| |
| protected: |
| BlockGraph block_graph_; |
| BlockGraph::Block* test_block_; |
| BasicBlock::Instructions instructions_; |
| BasicBlockAssembler asm_; |
| LivenessAnalysis liveness_; |
| LivenessAnalysis::State state_; |
| LivenessAnalysis::State defs_; |
| LivenessAnalysis::State uses_; |
| }; |
| |
| LivenessAnalysisTest::LivenessAnalysisTest() |
| : testing::Test(), |
| test_block_(NULL), |
| instructions_(), |
| asm_(instructions_.end(), &instructions_), |
| liveness_(), |
| state_() { |
| test_block_ = block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 10, "test block"); |
| } |
| |
| template <size_t N> |
| void LivenessAnalysisTest::UpdateDefsUsesFromBuffer(const uint8_t(&data)[N]) { |
| // Decode an instruction. |
| DCHECK_GT(assm::kMaxInstructionLength, N); |
| |
| block_graph::Instruction temp; |
| ASSERT_TRUE(block_graph::Instruction::FromBuffer(&data[0], N, &temp)); |
| |
| // Expect to decode the entire buffer. |
| ASSERT_TRUE(temp.size() == N); |
| |
| // Analyze the defs/uses of this instruction. |
| StateHelper::GetDefsOf(temp, &defs_); |
| StateHelper::GetUsesOf(temp, &uses_); |
| } |
| |
| template <size_t N> |
| void LivenessAnalysisTest::AddInstructionFromBuffer(const uint8_t(&data)[N]) { |
| // Decode an instruction and append it to basicblock_. |
| DCHECK_GT(assm::kMaxInstructionLength, N); |
| |
| block_graph::Instruction temp; |
| ASSERT_TRUE(block_graph::Instruction::FromBuffer(&data[0], N, &temp)); |
| |
| // Expect to decode the entire buffer. |
| ASSERT_TRUE(temp.size() == N); |
| |
| // Append this instruction to the basic block. |
| instructions_.push_back(temp); |
| } |
| |
| void LivenessAnalysisTest::DefineAllRegisters() { |
| // Inserts instructions into basicblock_ so all registers are defined. |
| AddInstructionFromBuffer(kMovEaxZero); |
| AddInstructionFromBuffer(kMovEbxZero); |
| AddInstructionFromBuffer(kMovEcxZero); |
| AddInstructionFromBuffer(kMovEdxZero); |
| AddInstructionFromBuffer(kMovEsiZero); |
| AddInstructionFromBuffer(kMovEdiZero); |
| AddInstructionFromBuffer(kMovEspZero); |
| AddInstructionFromBuffer(kMovEbpZero); |
| |
| // Define arithmetic flags. |
| AddInstructionFromBuffer(kCmpEaxEbx); |
| } |
| |
| void LivenessAnalysisTest::AnalyzeInstructionsWithoutReset() { |
| // Perform a backward liveness analysis on instructions in basicblock_. |
| // Results are kept in 'state_' and may be accessed through IsLive and |
| // AreArithmeticFlagsLive. |
| Instructions::reverse_iterator instr_iter = instructions_.rbegin(); |
| for (; instr_iter != instructions_.rend(); ++instr_iter) { |
| const Instruction& instr = *instr_iter; |
| liveness_.PropagateBackward(instr, &state_); |
| } |
| } |
| |
| void LivenessAnalysisTest::AnalyzeInstructions() { |
| LivenessAnalysis::StateHelper::SetAll(&state_); |
| AnalyzeInstructionsWithoutReset(); |
| } |
| |
| template <size_t N> |
| void LivenessAnalysisTest::AnalyzeSingleInstructionFromBuffer( |
| const uint8_t(&data)[N]) { |
| // This function creates a basic block with an instruction under test, |
| // followed by instructions to define all registers and flags. This way, the |
| // analysis may assume everything was dead before the instruction. |
| instructions_.clear(); |
| StateHelper::SetAll(&state_); |
| |
| AddInstructionFromBuffer(data); |
| DefineAllRegisters(); |
| AnalyzeInstructions(); |
| |
| // Retrieve defs/uses of this instruction. |
| UpdateDefsUsesFromBuffer(data); |
| } |
| |
| bool LivenessAnalysisTest::CheckCarryFlagInstruction( |
| bool expect_on, bool expect_off) { |
| LivenessAnalysis::State flags; |
| StateHelper::Clear(&flags); |
| StateHelper::SetFlags(static_cast<StateHelper::FlagsMask>(~(D_CF)), &state_); |
| |
| // Try with the carry flag on. |
| StateHelper::Clear(&state_); |
| StateHelper::SetFlags(D_CF, &state_); |
| AnalyzeInstructionsWithoutReset(); |
| StateHelper::Subtract(flags, &state_); |
| if (are_arithmetic_flags_live() != expect_on) |
| return false; |
| |
| // Try with the carry flag off. |
| StateHelper::Clear(&state_); |
| AnalyzeInstructionsWithoutReset(); |
| StateHelper::Subtract(flags, &state_); |
| if (are_arithmetic_flags_live() != expect_off) |
| return false; |
| |
| return true; |
| } |
| |
| void LivenessAnalysisTest::AddSuccessorBetween(Successor::Condition condition, |
| BasicCodeBlock* from, |
| BasicCodeBlock* to) { |
| from->successors().push_back( |
| Successor(condition, |
| BasicBlockReference(BlockGraph::RELATIVE_REF, |
| BlockGraph::Reference::kMaximumSize, |
| to), |
| 0)); |
| } |
| |
| TEST(LivenessAnalysisStateTest, StateRegisterMaskOperations) { |
| // On creation, a state assumes all registers are alive. |
| State state_full; |
| EXPECT_TRUE(StateHelper::IsSet(state_full, |
| static_cast<StateHelper::RegisterMask>(StateHelper::REGBITS_ALL))); |
| EXPECT_TRUE(StateHelper::IsSet(state_full, StateHelper::REGBITS_AX)); |
| |
| // The Clear operation should not keep any register partially defined. |
| State state_empty; |
| StateHelper::Clear(&state_empty); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_empty, |
| static_cast<StateHelper::RegisterMask>(StateHelper::REGBITS_ALL))); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_empty, StateHelper::REGBITS_AX)); |
| |
| // Test sub-registers definition. |
| State state_ax; |
| State state_cx; |
| StateHelper::Clear(&state_ax); |
| StateHelper::Clear(&state_cx); |
| StateHelper::Set(StateHelper::REGBITS_AX, &state_ax); |
| StateHelper::Set(StateHelper::REGBITS_CX, &state_cx); |
| EXPECT_TRUE(StateHelper::IsPartiallySet(state_ax, StateHelper::REGBITS_EAX)); |
| EXPECT_TRUE(StateHelper::IsSet(state_ax, StateHelper::REGBITS_AL)); |
| EXPECT_TRUE(StateHelper::IsSet(state_ax, StateHelper::REGBITS_AH)); |
| EXPECT_TRUE(StateHelper::IsSet(state_ax, StateHelper::REGBITS_AX)); |
| EXPECT_TRUE(StateHelper::IsPartiallySet(state_cx, StateHelper::REGBITS_ECX)); |
| EXPECT_TRUE(StateHelper::IsSet(state_cx, StateHelper::REGBITS_CL)); |
| EXPECT_TRUE(StateHelper::IsSet(state_cx, StateHelper::REGBITS_CH)); |
| EXPECT_TRUE(StateHelper::IsSet(state_cx, StateHelper::REGBITS_CX)); |
| |
| // Test IsLive operation. |
| EXPECT_TRUE(state_full.IsLive(assm::eax)); |
| EXPECT_TRUE(state_full.IsLive(assm::ecx)); |
| EXPECT_FALSE(state_empty.IsLive(assm::eax)); |
| EXPECT_FALSE(state_empty.IsLive(assm::ecx)); |
| EXPECT_TRUE(state_ax.IsLive(assm::eax)); |
| EXPECT_FALSE(state_ax.IsLive(assm::ecx)); |
| EXPECT_FALSE(state_cx.IsLive(assm::eax)); |
| EXPECT_TRUE(state_cx.IsLive(assm::ecx)); |
| |
| // Test copy constructor. |
| State state_copy(state_ax); |
| EXPECT_TRUE( |
| StateHelper::IsPartiallySet(state_copy, StateHelper::REGBITS_EAX)); |
| EXPECT_TRUE(StateHelper::IsSet(state_copy, StateHelper::REGBITS_AL)); |
| EXPECT_TRUE(StateHelper::IsSet(state_copy, StateHelper::REGBITS_AH)); |
| EXPECT_TRUE(StateHelper::IsSet(state_copy, StateHelper::REGBITS_AX)); |
| |
| // Test Copy operation. |
| State state_copy_ax; |
| StateHelper::Copy(state_ax, &state_copy_ax); |
| EXPECT_TRUE( |
| StateHelper::IsPartiallySet(state_copy_ax, StateHelper::REGBITS_EAX)); |
| EXPECT_TRUE(StateHelper::IsSet(state_copy_ax, StateHelper::REGBITS_AL)); |
| EXPECT_TRUE(StateHelper::IsSet(state_copy_ax, StateHelper::REGBITS_AH)); |
| EXPECT_TRUE(StateHelper::IsSet(state_copy_ax, StateHelper::REGBITS_AX)); |
| |
| // Test Union operation. |
| State state_merged; |
| StateHelper::Clear(&state_merged); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_AX)); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_CX)); |
| StateHelper::Union(state_ax, &state_merged); |
| EXPECT_TRUE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_AX)); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_CX)); |
| StateHelper::Union(state_cx, &state_merged); |
| EXPECT_TRUE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_AX)); |
| EXPECT_TRUE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_CX)); |
| |
| // Test Subtract operation |
| StateHelper::Subtract(state_ax, &state_merged); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_AX)); |
| EXPECT_TRUE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_CX)); |
| StateHelper::Subtract(state_cx, &state_merged); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_AX)); |
| EXPECT_FALSE( |
| StateHelper::IsPartiallySet(state_merged, StateHelper::REGBITS_CX)); |
| } |
| |
| TEST(LivenessAnalysisStateTest, StateFlagsMaskOperations) { |
| // On creation, a state assumes all flags are alive. |
| State state_full; |
| EXPECT_TRUE(state_full.AreArithmeticFlagsLive()); |
| |
| // The Clear operation should not keep any flags alive. |
| State state_empty; |
| StateHelper::Clear(&state_empty); |
| EXPECT_FALSE(state_empty.AreArithmeticFlagsLive()); |
| |
| // Partially defined flags must be considered alive. |
| State state_flagA; |
| State state_flagB; |
| State state_flagC; |
| StateHelper::Clear(&state_flagA); |
| StateHelper::Clear(&state_flagB); |
| StateHelper::SetFlags(0xF0F0, &state_flagA); |
| StateHelper::SetFlags(0xFFFF, &state_flagB); |
| |
| EXPECT_TRUE(state_flagA.AreArithmeticFlagsLive()); |
| EXPECT_TRUE(state_flagB.AreArithmeticFlagsLive()); |
| |
| // Test Subtract operation. |
| State state_flag_ari1; |
| State state_flag_ari2; |
| StateHelper::Clear(&state_flag_ari1); |
| StateHelper::Clear(&state_flag_ari2); |
| StateHelper::SetFlags(D_ZF | D_SF | D_CF, &state_flag_ari1); |
| StateHelper::SetFlags(D_OF | D_PF | D_AF, &state_flag_ari2); |
| |
| EXPECT_TRUE(state_flag_ari1.AreArithmeticFlagsLive()); |
| EXPECT_TRUE(state_flag_ari2.AreArithmeticFlagsLive()); |
| |
| State state_merged; |
| EXPECT_TRUE(state_merged.AreArithmeticFlagsLive()); |
| StateHelper::Subtract(state_flag_ari1, &state_merged); |
| EXPECT_TRUE(state_merged.AreArithmeticFlagsLive()); |
| StateHelper::Subtract(state_flag_ari2, &state_merged); |
| EXPECT_FALSE(state_merged.AreArithmeticFlagsLive()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, Mov1Analysis) { |
| asm_.mov(assm::eax, Immediate(10)); |
| asm_.mov(assm::ecx, assm::ebx); |
| AnalyzeInstructions(); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ax)); |
| EXPECT_FALSE(is_live(assm::ah)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::bx)); |
| EXPECT_TRUE(is_live(assm::bl)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, Mov2Analysis) { |
| asm_.mov(assm::eax, assm::ebx); |
| asm_.mov(assm::edx, Immediate(10)); |
| asm_.mov(assm::ecx, Immediate(test_block_, 0)); |
| AnalyzeInstructions(); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ax)); |
| EXPECT_FALSE(is_live(assm::ah)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::bx)); |
| EXPECT_TRUE(is_live(assm::bl)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, DefineAllRegisters) { |
| // Validate the tester by defining all registers and using none. |
| DefineAllRegisters(); |
| AnalyzeInstructions(); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ax)); |
| EXPECT_FALSE(is_live(assm::al)); |
| EXPECT_FALSE(is_live(assm::ah)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::bx)); |
| EXPECT_FALSE(is_live(assm::bl)); |
| EXPECT_FALSE(is_live(assm::bh)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::cx)); |
| EXPECT_FALSE(is_live(assm::cl)); |
| EXPECT_FALSE(is_live(assm::ch)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(is_live(assm::dx)); |
| EXPECT_FALSE(is_live(assm::dl)); |
| EXPECT_FALSE(is_live(assm::dh)); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_FALSE(is_live(assm::si)); |
| EXPECT_FALSE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::di)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, Defs1Analysis) { |
| // Validate the tester by defining all registers and using some of them. |
| AddInstructionFromBuffer(kMovEaxZero); |
| AddInstructionFromBuffer(kMovEcxZero); |
| AddInstructionFromBuffer(kMovEsiZero); |
| AnalyzeInstructions(); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ax)); |
| EXPECT_FALSE(is_live(assm::ah)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::bx)); |
| EXPECT_TRUE(is_live(assm::bl)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::cx)); |
| EXPECT_FALSE(is_live(assm::cl)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_TRUE(is_live(assm::dx)); |
| EXPECT_TRUE(is_live(assm::dl)); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_FALSE(is_live(assm::si)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_TRUE(is_live(assm::di)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, Defs2Analysis) { |
| // Validate the tester by defining all registers and using some of them. |
| AddInstructionFromBuffer(kMovEbxZero); |
| AddInstructionFromBuffer(kMovEdxZero); |
| AddInstructionFromBuffer(kMovEdiZero); |
| AnalyzeInstructions(); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ax)); |
| EXPECT_TRUE(is_live(assm::al)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::bx)); |
| EXPECT_FALSE(is_live(assm::bh)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::cx)); |
| EXPECT_TRUE(is_live(assm::cl)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(is_live(assm::dx)); |
| EXPECT_FALSE(is_live(assm::dl)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::si)); |
| EXPECT_FALSE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::di)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, Analysis16Bit) { |
| AddInstructionFromBuffer(kMovAxZero); |
| AnalyzeInstructions(); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ax)); |
| EXPECT_FALSE(is_live(assm::al)); |
| EXPECT_FALSE(is_live(assm::ah)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, Analysis8Bit) { |
| AddInstructionFromBuffer(kMovAlZero); |
| AnalyzeInstructions(); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ax)); |
| EXPECT_FALSE(is_live(assm::al)); |
| EXPECT_TRUE(is_live(assm::ah)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, OperandTypeLeft) { |
| // Validate the support of all DiStorm operand types (as first operand). |
| // _asm add eax, ecx |
| static const uint8_t kOpReg1[] = {0x03, 0xC1}; |
| AnalyzeSingleInstructionFromBuffer(kOpReg1); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add [eax], ecx |
| static const uint8_t kOpSmem[] = {0x01, 0x08}; |
| AnalyzeSingleInstructionFromBuffer(kOpSmem); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add [eax + 42], ecx |
| static const uint8_t kOpSmemOffet[] = {0x01, 0x48, 0x2A}; |
| AnalyzeSingleInstructionFromBuffer(kOpSmemOffet); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add [eax + ebx*2 + 42], ecx |
| static const uint8_t kOpMemOffset[] = {0x01, 0x4C, 0x58, 0x2A}; |
| AnalyzeSingleInstructionFromBuffer(kOpMemOffset); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add DWORD PTR [X], ecx |
| static const uint8_t kOpDispl[] = {0x01, 0x0D, 0x80, 0x1E, 0xF2, 0x00}; |
| AnalyzeSingleInstructionFromBuffer(kOpDispl); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, OperandTypeRight) { |
| // Validate the support of all DiStorm operand types (as second operand). |
| // _asm add ecx, 1 |
| static const uint8_t kOpReg1[] = {0x83, 0xC1, 0x01}; |
| AnalyzeSingleInstructionFromBuffer(kOpReg1); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add ecx, eax |
| static const uint8_t kOpReg2[] = {0x03, 0xC8}; |
| AnalyzeSingleInstructionFromBuffer(kOpReg2); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add ecx, [eax] |
| static const uint8_t kOpSmem[] = {0x03, 0x08}; |
| AnalyzeSingleInstructionFromBuffer(kOpSmem); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add ecx, [eax + 42] |
| static const uint8_t kOpSmemOffet[] = {0x03, 0x48, 0x2A}; |
| AnalyzeSingleInstructionFromBuffer(kOpSmemOffet); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add ecx, [eax + ebx*2 + 42] |
| static const uint8_t kOpMemOffset[] = {0x03, 0x4C, 0x58, 0x2A}; |
| AnalyzeSingleInstructionFromBuffer(kOpMemOffset); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm add ecx, DWORD PTR [X] |
| static const uint8_t kOpDispl[] = {0x03, 0x0D, 0x80, 0x1E, 0x27, 0x00}; |
| AnalyzeSingleInstructionFromBuffer(kOpDispl); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, InstructionWithoutDefine) { |
| // Validate instructions that fully overwrite and use the destination. |
| // _asm cmp eax, [ecx] |
| static const uint8_t kCmp[] = {0x3B, 0x01}; |
| AnalyzeSingleInstructionFromBuffer(kCmp); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm test ebx, [edx+12] |
| static const uint8_t kTest[] = {0x85, 0x5A, 0x0C}; |
| AnalyzeSingleInstructionFromBuffer(kTest); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, InstructionsWithDefine) { |
| // Validate instructions that fully overwrite the destination. |
| // _asm mov ebx, [edx+12] |
| static const uint8_t kCmp[] = {0x8B, 0x5A, 0x0C}; |
| AnalyzeSingleInstructionFromBuffer(kCmp); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| |
| // _asm lea ebx, [edx+12] |
| static const uint8_t kTest[] = {0x8D, 0x5A, 0x0C}; |
| AnalyzeSingleInstructionFromBuffer(kTest); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, InstructionsWithPartialDefine) { |
| // Registers partially defined must be considered alive. |
| // _asm mov bl, dl |
| static const uint8_t kCmp[] = {0xB3, 0x0C}; |
| // _asm mov DWORD PTR [X], ebx |
| static const uint8_t kStore[] = {0x89, 0x1D, 0x80, 0x1E, 0x10, 0x01}; |
| AddInstructionFromBuffer(kCmp); |
| AddInstructionFromBuffer(kStore); |
| AnalyzeInstructions(); |
| |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::bx)); |
| EXPECT_FALSE(is_live(assm::bl)); |
| EXPECT_TRUE(is_live(assm::bh)); |
| |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_TRUE(is_live(assm::dx)); |
| EXPECT_TRUE(is_live(assm::dl)); |
| EXPECT_TRUE(is_live(assm::dh)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, InstructionsWithPartialDefineAll) { |
| static const uint8_t kMovAl[] = {0xB0, 0x00}; |
| static const uint8_t kMovBl[] = {0xB1, 0x00}; |
| static const uint8_t kMovCl[] = {0xB2, 0x00}; |
| static const uint8_t kMovDl[] = {0xB3, 0x00}; |
| static const uint8_t kMovAh[] = {0xB4, 0x00}; |
| static const uint8_t kMovBh[] = {0xB7, 0x00}; |
| static const uint8_t kMovCh[] = {0xB5, 0x00}; |
| static const uint8_t kMovDh[] = {0xB6, 0x00}; |
| static const uint8_t kMovAx[] = {0x66, 0xB8, 0x00, 0x00}; |
| static const uint8_t kMovBx[] = {0x66, 0xBB, 0x00, 0x00}; |
| static const uint8_t kMovCx[] = {0x66, 0xB9, 0x00, 0x00}; |
| static const uint8_t kMovDx[] = {0x66, 0xBA, 0x00, 0x00}; |
| static const uint8_t kMovSi[] = {0x66, 0xBE, 0x00, 0x00}; |
| static const uint8_t kMovDi[] = {0x66, 0xBF, 0x00, 0x00}; |
| static const uint8_t kMovSp[] = {0x66, 0xBC, 0x00, 0x00}; |
| static const uint8_t kMovBp[] = {0x66, 0xBD, 0x00, 0x00}; |
| |
| // 8-bit partial registers. |
| AddInstructionFromBuffer(kMovAl); |
| AddInstructionFromBuffer(kMovBl); |
| AddInstructionFromBuffer(kMovCl); |
| AddInstructionFromBuffer(kMovDl); |
| |
| AddInstructionFromBuffer(kMovAh); |
| AddInstructionFromBuffer(kMovBh); |
| AddInstructionFromBuffer(kMovCh); |
| AddInstructionFromBuffer(kMovDh); |
| |
| // 16-bit partial registers. |
| AddInstructionFromBuffer(kMovAx); |
| AddInstructionFromBuffer(kMovBx); |
| AddInstructionFromBuffer(kMovCx); |
| AddInstructionFromBuffer(kMovDx); |
| |
| AddInstructionFromBuffer(kMovSi); |
| AddInstructionFromBuffer(kMovDi); |
| AddInstructionFromBuffer(kMovSp); |
| AddInstructionFromBuffer(kMovBp); |
| |
| AnalyzeInstructions(); |
| |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_live(assm::ebp)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, ArithmeticUnaryInstructions) { |
| // _asm dec eax |
| static const uint8_t kDec1[] = {0x48}; |
| AnalyzeSingleInstructionFromBuffer(kDec1); |
| EXPECT_TRUE(is_live(assm::eax)); |
| |
| // _asm dec [ebx + 1] |
| static const uint8_t kDec2[] = {0xFE, 0x4B, 0x01}; |
| AnalyzeSingleInstructionFromBuffer(kDec2); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm dec [esi + ebx*2 + 1] |
| static const uint8_t kDec3[] = {0xFE, 0x4C, 0x5E, 0x01}; |
| AnalyzeSingleInstructionFromBuffer(kDec3); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm dec WORD PTR [X] |
| static const uint8_t kDec4[] = {0x66, 0xFF, 0x0D, 0x80, 0x1E, 0x92, 0x00}; |
| AnalyzeSingleInstructionFromBuffer(kDec4); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| |
| // _asm not ebx |
| static const uint8_t kNot1[] = {0xF7, 0xD3}; |
| AnalyzeSingleInstructionFromBuffer(kNot1); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm not [ebx] |
| static const uint8_t kNot2[] = {0xF6, 0x13}; |
| AnalyzeSingleInstructionFromBuffer(kNot2); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm neg ebx |
| static const uint8_t kNeg1[] = {0xF7, 0xDB}; |
| AnalyzeSingleInstructionFromBuffer(kNeg1); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm neg [ebx] |
| static const uint8_t kNeg2[] = {0xF6, 0x1B}; |
| AnalyzeSingleInstructionFromBuffer(kNeg2); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm inc edx |
| static const uint8_t kInc[] = {0x42}; |
| AnalyzeSingleInstructionFromBuffer(kInc); |
| EXPECT_TRUE(is_live(assm::edx)); |
| |
| // _asm inc dh |
| static const uint8_t kIncHalf[] = {0xFE, 0xC6}; |
| AnalyzeSingleInstructionFromBuffer(kIncHalf); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_TRUE(is_def(assm::dh)); |
| EXPECT_FALSE(is_def(assm::dl)); |
| EXPECT_TRUE(is_use(assm::dh)); |
| EXPECT_FALSE(is_use(assm::dl)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, DecIncFlagsInstructions) { |
| // NOTE: inc/dec do not touch the carry flag. |
| // _asm inc edx |
| static const uint8_t kInc[] = {0x42}; |
| AddInstructionFromBuffer(kInc); |
| EXPECT_TRUE(CheckCarryFlagInstruction(true, false)); |
| instructions_.clear(); |
| |
| // _asm dec eax |
| static const uint8_t kDec1[] = {0x48}; |
| AddInstructionFromBuffer(kDec1); |
| EXPECT_TRUE(CheckCarryFlagInstruction(true, false)); |
| instructions_.clear(); |
| } |
| |
| TEST_F(LivenessAnalysisTest, ArithmeticBinaryInstructions) { |
| // _asm add ebx, ecx |
| static const uint8_t kAdd[] = {0x03, 0xD9}; |
| AnalyzeSingleInstructionFromBuffer(kAdd); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_def(assm::ebx)); |
| EXPECT_TRUE(is_use(assm::ebx)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| |
| // _asm adc ebx, edx |
| static const uint8_t kAdc[] = {0x13, 0xDA}; |
| AnalyzeSingleInstructionFromBuffer(kAdc); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| |
| // _asm sub esi, edi |
| static const uint8_t kSub[] = {0x2B, 0xF7}; |
| AnalyzeSingleInstructionFromBuffer(kSub); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| |
| // _asm sbb ebx, [eax + edx + 12] |
| static const uint8_t KSbb[] = {0x1B, 0x5C, 0x10, 0x0C}; |
| AnalyzeSingleInstructionFromBuffer(KSbb); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| |
| // _asm and ebx, ecx |
| static const uint8_t kAnd[] = {0x23, 0xD9}; |
| AnalyzeSingleInstructionFromBuffer(kAnd); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm or esi, [edi] |
| static const uint8_t kOr[] = {0x0B, 0x37}; |
| AnalyzeSingleInstructionFromBuffer(kOr); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| |
| // _asm xor [esi], edi |
| static const uint8_t kXor[] = {0x31, 0x3E}; |
| AnalyzeSingleInstructionFromBuffer(kXor); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| |
| // _asm shl ebx, 1 |
| static const uint8_t kShl1[] = {0xD1, 0xE3}; |
| AnalyzeSingleInstructionFromBuffer(kShl1); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm shr esi, 2 |
| static const uint8_t kShr1[] = {0xC1, 0xEE, 0x02}; |
| AnalyzeSingleInstructionFromBuffer(kShr1); |
| EXPECT_TRUE(is_live(assm::esi)); |
| |
| // _asm sar ecx, 3 |
| static const uint8_t kSar1[] = {0xC1, 0xF9, 0x03}; |
| AnalyzeSingleInstructionFromBuffer(kSar1); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm rol ebx, 1 |
| static const uint8_t kRol1[] = {0xD1, 0xC3}; |
| AnalyzeSingleInstructionFromBuffer(kRol1); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| |
| // _asm ror esi, 2 |
| static const uint8_t kRor1[] = {0xC1, 0xCE, 0x02}; |
| AnalyzeSingleInstructionFromBuffer(kRor1); |
| EXPECT_TRUE(is_live(assm::esi)); |
| |
| // _asm shl ebx, cl |
| static const uint8_t kShl2[] = {0xD3, 0xE3}; |
| AnalyzeSingleInstructionFromBuffer(kShl2); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm shr esi, cl |
| static const uint8_t kShr2[] = {0xD3, 0xEE}; |
| AnalyzeSingleInstructionFromBuffer(kShr2); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm sar edx, cl |
| static const uint8_t kSar2[] = {0xD3, 0xFA}; |
| AnalyzeSingleInstructionFromBuffer(kSar2); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm rol ebx, cl |
| static const uint8_t kRol2[] = {0xD3, 0xC3}; |
| AnalyzeSingleInstructionFromBuffer(kRol2); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm ror esi, cl |
| static const uint8_t kRor2[] = {0xD3, 0xCE}; |
| AnalyzeSingleInstructionFromBuffer(kRor2); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, ArithmeticFlagsInstructions) { |
| // _asm adc ebx, edx |
| static const uint8_t kAdc[] = {0x13, 0xDA}; |
| AnalyzeSingleInstructionFromBuffer(kAdc); |
| EXPECT_TRUE(CheckCarryFlagInstruction(true, true)); |
| |
| // _asm sbb ebx, [eax + edx + 12] |
| static const uint8_t KSbb[] = {0x1B, 0x5C, 0x10, 0x0C}; |
| AnalyzeSingleInstructionFromBuffer(KSbb); |
| EXPECT_TRUE(CheckCarryFlagInstruction(true, true)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, MultiplicationInstructions) { |
| // _asm mul ecx |
| static const uint8_t kMul32[] = {0xF7, 0xE1}; |
| AnalyzeSingleInstructionFromBuffer(kMul32); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_FALSE(is_def(assm::ecx)); |
| EXPECT_TRUE(is_def(assm::edx)); |
| EXPECT_TRUE(is_use(assm::eax)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| EXPECT_FALSE(is_use(assm::edx)); |
| |
| // _asm mul cx |
| static const uint8_t kMul16[] = {0x66, 0xF7, 0xE1}; |
| AnalyzeSingleInstructionFromBuffer(kMul16); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_FALSE(is_def(assm::ecx)); |
| EXPECT_TRUE(is_use(assm::eax)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| |
| // _asm mul cl |
| static const uint8_t kMul8[] = {0xF6, 0xE1}; |
| AnalyzeSingleInstructionFromBuffer(kMul8); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(is_live(assm::ah)); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_TRUE(is_def(assm::ah)); |
| EXPECT_FALSE(is_def(assm::ecx)); |
| EXPECT_FALSE(is_def(assm::cl)); |
| EXPECT_TRUE(is_use(assm::eax)); |
| EXPECT_FALSE(is_use(assm::ah)); |
| EXPECT_TRUE(is_use(assm::al)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| EXPECT_FALSE(is_use(assm::ch)); |
| EXPECT_TRUE(is_use(assm::cl)); |
| |
| // _asm mul ah |
| static const uint8_t kMul16High[] = {0xF6, 0xE4}; |
| AnalyzeSingleInstructionFromBuffer(kMul16High); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_TRUE(is_live(assm::ah)); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_TRUE(is_def(assm::ah)); |
| EXPECT_TRUE(is_def(assm::al)); |
| EXPECT_FALSE(is_def(assm::dl)); |
| EXPECT_TRUE(is_use(assm::ah)); |
| EXPECT_TRUE(is_use(assm::al)); |
| EXPECT_FALSE(is_use(assm::dl)); |
| |
| // _asm imul ecx |
| static const uint8_t kIMul32[] = {0xF7, 0xE9}; |
| AnalyzeSingleInstructionFromBuffer(kIMul32); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_TRUE(is_def(assm::al)); |
| EXPECT_TRUE(is_def(assm::dl)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| |
| // _asm imul cx |
| static const uint8_t kIMul16[] = {0xF7, 0xE9}; |
| AnalyzeSingleInstructionFromBuffer(kIMul16); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_TRUE(is_def(assm::al)); |
| EXPECT_TRUE(is_def(assm::dl)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| |
| // _asm imul cl |
| static const uint8_t kIMul8[] = {0xF6, 0xE9}; |
| AnalyzeSingleInstructionFromBuffer(kIMul8); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(is_live(assm::ch)); |
| EXPECT_TRUE(is_def(assm::al)); |
| EXPECT_FALSE(is_def(assm::dl)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| |
| // _asm imul ah |
| static const uint8_t kIMul16High[] = {0xF6, 0xEC}; |
| AnalyzeSingleInstructionFromBuffer(kIMul16High); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_TRUE(is_live(assm::ah)); |
| EXPECT_TRUE(is_def(assm::al)); |
| EXPECT_TRUE(is_def(assm::ah)); |
| EXPECT_TRUE(is_use(assm::al)); |
| EXPECT_TRUE(is_use(assm::ah)); |
| EXPECT_FALSE(is_def(assm::dl)); |
| |
| // _asm imul eax, 3 |
| static const uint8_t kIMul32ByCst[] = {0x6B, 0xC0, 0x03}; |
| AnalyzeSingleInstructionFromBuffer(kIMul32ByCst); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_TRUE(is_use(assm::eax)); |
| EXPECT_FALSE(is_def(assm::dl)); |
| |
| // _asm imul ecx, 3 |
| static const uint8_t kIMul32EcxByCst[] = {0x6B, 0xC9, 0x03}; |
| AnalyzeSingleInstructionFromBuffer(kIMul32EcxByCst); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(is_def(assm::eax)); |
| EXPECT_TRUE(is_def(assm::ecx)); |
| EXPECT_FALSE(is_def(assm::dl)); |
| EXPECT_FALSE(is_use(assm::eax)); |
| EXPECT_TRUE(is_use(assm::ecx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, ConversionInstructions) { |
| static const uint8_t kCdq[] = {0x99}; |
| AnalyzeSingleInstructionFromBuffer(kCdq); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_TRUE(is_def(assm::edx)); |
| EXPECT_TRUE(is_use(assm::eax)); |
| EXPECT_FALSE(is_use(assm::edx)); |
| |
| static const uint8_t kCwd[] = {0x66, 0x99}; |
| AnalyzeSingleInstructionFromBuffer(kCwd); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_FALSE(is_def(assm::edx)); |
| EXPECT_TRUE(is_use(assm::eax)); |
| EXPECT_FALSE(is_use(assm::edx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, EpilogueInstructions) { |
| static const uint8_t kLeave[] = {0xC9}; |
| AnalyzeSingleInstructionFromBuffer(kLeave); |
| EXPECT_TRUE(is_live(assm::ebp)); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_def(assm::ebp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, StackInstructions) { |
| // Validate instructions that push/pop on the stack. |
| // _asm push eax |
| static const uint8_t kPushd[] = {0x50}; |
| AnalyzeSingleInstructionFromBuffer(kPushd); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| |
| // _asm pop eax |
| static const uint8_t kPopd[] = {0x58}; |
| AnalyzeSingleInstructionFromBuffer(kPopd); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| |
| // _asm push ax |
| static const uint8_t kPush[] = {0x66, 0x50}; |
| AnalyzeSingleInstructionFromBuffer(kPush); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| |
| // _asm pop ax |
| static const uint8_t kPop[] = {0x66, 0x58}; |
| AnalyzeSingleInstructionFromBuffer(kPop); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| |
| static const uint8_t kPopSMem[] = {0x66, 0x8F, 0x00}; |
| AnalyzeSingleInstructionFromBuffer(kPopSMem); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, SetFlagInstructions) { |
| // Validate instructions that consume flags. Ensure flags are used. |
| |
| // _asm seta al |
| static const uint8_t kSetA[] = {0x0F, 0x97, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetA); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::al)); |
| EXPECT_FALSE(is_use(assm::al)); |
| |
| // _asm setae al |
| static const uint8_t kSetAE[] = {0x0F, 0x93, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetAE); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setb al |
| static const uint8_t kSetB[] = {0x0F, 0x92, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetB); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setbe al |
| static const uint8_t kSetBE[] = {0x0F, 0x96, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetBE); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setg al |
| static const uint8_t kSetG[] = {0x0F, 0x9F, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetG); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setge al |
| static const uint8_t kSetGE[] = {0x0F, 0x9D, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetGE); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setl al |
| static const uint8_t kSetL[] = {0x0F, 0x9C, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetL); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setle al |
| static const uint8_t kSetLE[] = {0x0F, 0x9E, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetLE); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setno al |
| static const uint8_t kSetNO[] = {0x0F, 0x91, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetNO); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setnp al |
| static const uint8_t kSetNP[] = {0x0F, 0x9B, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetNP); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setns al |
| static const uint8_t kSetNS[] = {0x0F, 0x99, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetNS); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setnz al |
| static const uint8_t kSetNZ[] = {0x0F, 0x95, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetNZ); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm seto al |
| static const uint8_t kSetO[] = {0x0F, 0x90, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetO); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setp al |
| static const uint8_t kSetP[] = {0x0F, 0x9A, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetP); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm sets al |
| static const uint8_t kSetS[] = {0x0F, 0x98, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetS); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| |
| // _asm setz al |
| static const uint8_t kSetZ[] = {0x0F, 0x94, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kSetZ); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, PushPopFlagsInstructions) { |
| // Validate instructions that push/pop flags. Ensure flags are used, and stack |
| // pointer is modified. |
| |
| // _asm pushfd |
| static const uint8_t kPushfd[] = {0x9C}; |
| AnalyzeSingleInstructionFromBuffer(kPushfd); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| |
| // _asm popfd |
| static const uint8_t kPopfd[] = {0x9D}; |
| AnalyzeSingleInstructionFromBuffer(kPopfd); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| |
| // _asm pushf |
| static const uint8_t kPushf[] = {0x66, 0x9C}; |
| AnalyzeSingleInstructionFromBuffer(kPushf); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| |
| // _asm popf |
| static const uint8_t kPopf[] = {0x66, 0x9D}; |
| AnalyzeSingleInstructionFromBuffer(kPopf); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_live(assm::esp)); |
| EXPECT_TRUE(is_def(assm::esp)); |
| EXPECT_TRUE(is_use(assm::esp)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, LoadStoreFlagsInstructions) { |
| // Validate instructions that load/store flags. Ensure flags are defined or |
| // used, and stack pointer is not modified. |
| |
| // _asm sahf |
| static const uint8_t kSahf[] = {0x9E}; |
| AnalyzeSingleInstructionFromBuffer(kSahf); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_live(assm::esp)); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_def(assm::ah)); |
| EXPECT_TRUE(is_use(assm::ah)); |
| |
| // _asm lahf |
| static const uint8_t kLahf[] = {0x9F}; |
| AnalyzeSingleInstructionFromBuffer(kLahf); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_live(assm::esp)); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_def(assm::ah)); |
| EXPECT_FALSE(is_use(assm::ah)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, ExtendMovInstructions) { |
| // _asm movsx eax, cl |
| static const uint8_t kMovsx1[] = {0x0F, 0xBE, 0xC1}; |
| AnalyzeSingleInstructionFromBuffer(kMovsx1); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_def(assm::ah)); |
| EXPECT_FALSE(is_use(assm::ch)); |
| |
| // _asm movsx eax, BYTE PTR [ecx] |
| static const uint8_t kMovsx2[] = {0x0F, 0xBE, 0x01}; |
| AnalyzeSingleInstructionFromBuffer(kMovsx2); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm movzx eax, cl |
| static const uint8_t kMovzx1[] = {0x0F, 0xB6, 0xC1}; |
| AnalyzeSingleInstructionFromBuffer(kMovzx1); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| |
| // _asm movzx eax, BYTE PTR [ecx] |
| static const uint8_t kMovzx2[] = {0x0F, 0xB6, 0x01}; |
| AnalyzeSingleInstructionFromBuffer(kMovzx2); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, StringInstructions) { |
| // movs dword ptr es:[edi], dword ptr [esi] |
| static const uint8_t kMovsl[] = {0xA5}; |
| AnalyzeSingleInstructionFromBuffer(kMovsl); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::esi)); |
| EXPECT_TRUE(is_def(assm::edi)); |
| EXPECT_TRUE(is_use(assm::esi)); |
| EXPECT_TRUE(is_use(assm::edi)); |
| |
| // movs byte ptr es:[edi], byte ptr [esi] |
| static const uint8_t kMovsb[] = {0xA4}; |
| AnalyzeSingleInstructionFromBuffer(kMovsb); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // stos dword ptr es:[edi] |
| static const uint8_t kStosl[] = {0xAB}; |
| AnalyzeSingleInstructionFromBuffer(kStosl); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_FALSE(is_def(assm::esi)); |
| EXPECT_TRUE(is_def(assm::edi)); |
| EXPECT_FALSE(is_use(assm::esi)); |
| EXPECT_TRUE(is_use(assm::edi)); |
| |
| // stos byte ptr es:[edi] |
| static const uint8_t Stosb[] = {0xAA}; |
| AnalyzeSingleInstructionFromBuffer(Stosb); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, PrefixedStringInstructions) { |
| // repne movs dword ptr es:[edi], dword ptr [esi] |
| static const uint8_t kMovsl[] = {0xF2, 0xA5}; |
| AnalyzeSingleInstructionFromBuffer(kMovsl); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // repne movs byte ptr es:[edi], byte ptr [esi] |
| static const uint8_t kMovsb[] = {0xF2, 0xA4}; |
| AnalyzeSingleInstructionFromBuffer(kMovsb); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // repne stos dword ptr es:[edi] |
| static const uint8_t kStosl[] = {0xF2, 0xAB}; |
| AnalyzeSingleInstructionFromBuffer(kStosl); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // repne stos byte ptr es:[edi] |
| static const uint8_t Stosb[] = {0xF2, 0xAA}; |
| AnalyzeSingleInstructionFromBuffer(Stosb); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, FloatingPointInstructions) { |
| // _asm fld1 |
| static const uint8_t kFld1[] = {0xD9, 0xE8}; |
| // _asm fldz |
| static const uint8_t kFldz[] = {0xD9, 0xEE}; |
| // _asm fadd |
| static const uint8_t kFadd[] = {0xDE, 0xC1}; |
| // _asm faddp st(3), st(0) |
| static const uint8_t kFaddp[] = {0xDE, 0xC3}; |
| // _asm fsub |
| static const uint8_t kFsub[] = {0xDE, 0xE9}; |
| // _asm fsubp st(3), st(0) |
| static const uint8_t kFsubp[] = {0xDE, 0xEB}; |
| // _asm fmul |
| static const uint8_t kFmul[] = {0xDE, 0xC9}; |
| // _asm fmulp st(3), st(0) |
| static const uint8_t kFmulp[] = {0xDE, 0xCB}; |
| |
| // Floating point instructions don't touch any register nor general registers. |
| AddInstructionFromBuffer(kFld1); |
| AddInstructionFromBuffer(kFldz); |
| AddInstructionFromBuffer(kFadd); |
| AddInstructionFromBuffer(kFaddp); |
| AddInstructionFromBuffer(kFsub); |
| AddInstructionFromBuffer(kFsubp); |
| AddInstructionFromBuffer(kFmul); |
| AddInstructionFromBuffer(kFmulp); |
| DefineAllRegisters(); |
| AnalyzeInstructions(); |
| |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_FALSE(is_live(assm::edi)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, FloatingPointMemoryInstructions) { |
| // _asm fld DWORD PTR [eax + ecx] |
| static const uint8_t kFld[] = {0xD9, 0x04, 0x08}; |
| AnalyzeSingleInstructionFromBuffer(kFld); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fst DWORD PTR [eax + ecx] |
| static const uint8_t kFst[] = {0xD9, 0x14, 0x08}; |
| AnalyzeSingleInstructionFromBuffer(kFst); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fstp DWORD PTR [eax + ecx] |
| static const uint8_t kFstp[] = {0xD9, 0x1C, 0x08}; |
| AnalyzeSingleInstructionFromBuffer(kFstp); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fadd DWORD PTR [eax] |
| static const uint8_t kFadd[] = {0xD8, 0x00}; |
| AnalyzeSingleInstructionFromBuffer(kFadd); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fsub DWORD PTR [ecx] |
| static const uint8_t kFsub[] = {0xD8, 0x21}; |
| AnalyzeSingleInstructionFromBuffer(kFsub); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fmul DWORD PTR [esi] |
| static const uint8_t kFmul[] = {0xD8, 0x0E}; |
| AnalyzeSingleInstructionFromBuffer(kFmul); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fild DWORD PTR [eax] |
| static const uint8_t kFild[] = {0xDB, 0x00}; |
| AnalyzeSingleInstructionFromBuffer(kFild); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fist DWORD PTR [eax] |
| static const uint8_t kFist[] = {0xDB, 0x10}; |
| AnalyzeSingleInstructionFromBuffer(kFist); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fistp DWORD PTR [eax] |
| static const uint8_t kFistp[] = {0xDB, 0x18}; |
| AnalyzeSingleInstructionFromBuffer(kFistp); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, FloatingPointCompareInstructions) { |
| // _asm fcom |
| static const uint8_t kFcom[] = {0xD8, 0xD1}; |
| AnalyzeSingleInstructionFromBuffer(kFcom); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fcomp |
| static const uint8_t kFcomp[] = {0xD8, 0xD9}; |
| AnalyzeSingleInstructionFromBuffer(kFcomp); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fcompp |
| static const uint8_t kFcompp[] = {0xDE, 0xD9}; |
| AnalyzeSingleInstructionFromBuffer(kFcompp); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fcomi |
| static const uint8_t kFcomi[] = {0xDB, 0xF1}; |
| AnalyzeSingleInstructionFromBuffer(kFcomi); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fcomip |
| static const uint8_t fcomip[] = {0xDF, 0xF1}; |
| AnalyzeSingleInstructionFromBuffer(fcomip); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, FloatingPointCompareMemoryInstructions) { |
| // _asm fcom qword ptr [edx+ecx*8] |
| static const uint8_t kFcom[] = {0xDC, 0x14, 0xCA}; |
| AnalyzeSingleInstructionFromBuffer(kFcom); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm fcomp word ptr [edx+ecx*8] |
| static const uint8_t kFcomp[] = {0xDC, 0x1C, 0xCA}; |
| AnalyzeSingleInstructionFromBuffer(kFcomp); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm ficom qword ptr [edx+ecx*8] |
| static const uint8_t kFicom[] = {0xDE, 0x14, 0xCA}; |
| AnalyzeSingleInstructionFromBuffer(kFicom); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm ficomp word ptr [edx+ecx*8] |
| static const uint8_t kFicomp[] = {0xDE, 0x1C, 0xCA}; |
| AnalyzeSingleInstructionFromBuffer(kFicomp); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm ficom dword ptr [eax] |
| static const uint8_t kFicom2[] = {0xDA, 0x10}; |
| AnalyzeSingleInstructionFromBuffer(kFicom2); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| |
| // _asm ficomp dword ptr [eax] |
| static const uint8_t ficomp2[] = {0xDA, 0x18}; |
| AnalyzeSingleInstructionFromBuffer(ficomp2); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_FALSE(is_live(assm::edx)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, FloatingPointCompareWithFlagsInstructions) { |
| // Some floating point operations modify eflags. |
| |
| // _asm fcomi |
| static const uint8_t kFcomi[] = {0xDB, 0xF1}; |
| AddInstructionFromBuffer(kFcomi); |
| EXPECT_TRUE(CheckCarryFlagInstruction(false, false)); |
| instructions_.clear(); |
| |
| // _asm fcomip |
| static const uint8_t fcomip[] = {0xDF, 0xF1}; |
| AddInstructionFromBuffer(fcomip); |
| EXPECT_TRUE(CheckCarryFlagInstruction(false, false)); |
| instructions_.clear(); |
| } |
| |
| TEST_F(LivenessAnalysisTest, UnknownInstruction) { |
| // Ensure unknown instructions are processed correctly. |
| static const uint8_t kRdtsc[] = {0x0F, 0x31}; |
| AnalyzeSingleInstructionFromBuffer(kRdtsc); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::ebp)); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, XorInitializationSpecialCase) { |
| // Validate an initialization pattern used by x86 compiler. |
| // Ensure the flags are assumed modified, and the register is unused. |
| |
| // _asm xor eax, eax |
| static const uint8_t kXor1[] = {0x33, 0xC0}; |
| AnalyzeSingleInstructionFromBuffer(kXor1); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::eax)); |
| EXPECT_FALSE(is_use(assm::eax)); |
| |
| // _asm xor ebx, ebx |
| static const uint8_t kXor2[] = {0x33, 0xDB}; |
| AnalyzeSingleInstructionFromBuffer(kXor2); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::ebx)); |
| EXPECT_FALSE(is_use(assm::ebx)); |
| |
| // _asm xor ecx, ecx |
| static const uint8_t kXor3[] = {0x33, 0xC9}; |
| AnalyzeSingleInstructionFromBuffer(kXor3); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_FALSE(are_arithmetic_flags_live()); |
| EXPECT_TRUE(is_def(assm::ecx)); |
| EXPECT_FALSE(is_use(assm::ecx)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, NopInstructionSpecialCase) { |
| // Nop should be ignored by the analysis. |
| asm_.mov(assm::eax, assm::eax); |
| asm_.mov(assm::eax, Immediate(10)); |
| AnalyzeInstructions(); |
| EXPECT_FALSE(is_live(assm::eax)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, GetStateAtEntryOfWithNull) { |
| // It is valid to pass a NULL pointer to get a state. |
| liveness_.GetStateAtEntryOf(NULL, &state_); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, GetStateAtExitOfWithNull) { |
| // It is valid to pass a NULL pointer to get a state. |
| liveness_.GetStateAtExitOf(NULL, &state_); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(are_arithmetic_flags_live()); |
| } |
| |
| TEST_F(LivenessAnalysisTest, LivenessAnalysisOverControlFlow) { |
| BasicBlockSubGraph subgraph; |
| |
| // Build and analyze this flow graph: |
| // [if1] |
| // / \ |
| // / \ |
| // [true1] [false1] |
| // mov esi, 1 mov esi, 2 |
| // mov edi, 2 |
| // \ / |
| // \ / |
| // [if2] <----------- |
| // / \ \ |
| // / \ \ |
| // [true2] [false2] \ |
| // mov eax, ebx mov ebp, esi | |
| // mov esi, edi | |
| // mov edi, ebp | |
| // mov eax, [esi] | |
| // \ / | |
| // \ / | |
| // [end2] / |
| // mov ecx, eax / |
| // \ / |
| // -----------------/ |
| |
| // Create the control flow graph. |
| BasicCodeBlock* if1 = subgraph.AddBasicCodeBlock("if1"); |
| BasicCodeBlock* true1 = subgraph.AddBasicCodeBlock("true1"); |
| BasicCodeBlock* false1 = subgraph.AddBasicCodeBlock("false1"); |
| BasicCodeBlock* if2 = subgraph.AddBasicCodeBlock("if2"); |
| BasicCodeBlock* true2 = subgraph.AddBasicCodeBlock("true2"); |
| BasicCodeBlock* false2 = subgraph.AddBasicCodeBlock("false2"); |
| BasicCodeBlock* end2 = subgraph.AddBasicCodeBlock("end2"); |
| |
| ASSERT_TRUE(if1 != NULL); |
| ASSERT_TRUE(true1 != NULL); |
| ASSERT_TRUE(false1 != NULL); |
| ASSERT_TRUE(if2 != NULL); |
| ASSERT_TRUE(true2 != NULL); |
| ASSERT_TRUE(false2 != NULL); |
| ASSERT_TRUE(end2 != NULL); |
| |
| AddSuccessorBetween(Successor::kConditionEqual, if1, true1); |
| AddSuccessorBetween(Successor::kConditionNotEqual, if1, false1); |
| AddSuccessorBetween(Successor::kConditionTrue, true1, if2); |
| AddSuccessorBetween(Successor::kConditionTrue, false1, if2); |
| |
| AddSuccessorBetween(Successor::kConditionOverflow, if2, true2); |
| AddSuccessorBetween(Successor::kConditionNotOverflow, if2, false2); |
| AddSuccessorBetween(Successor::kConditionLess, true2, end2); |
| AddSuccessorBetween(Successor::kConditionLess, false2, end2); |
| |
| AddSuccessorBetween(Successor::kConditionTrue, end2, if2); |
| |
| // Insert instructions into basic blocks. |
| BasicBlockAssembler asm_end2(end2->instructions().end(), |
| &end2->instructions()); |
| asm_end2.mov(assm::ecx, assm::eax); |
| |
| BasicBlockAssembler asm_true2(true2->instructions().end(), |
| &true2->instructions()); |
| asm_true2.mov(assm::eax, assm::ebx); |
| |
| BasicBlockAssembler asm_false2(false2->instructions().end(), |
| &false2->instructions()); |
| asm_false2.mov(assm::ebp, assm::esi); |
| asm_false2.mov(assm::esi, assm::edi); |
| asm_false2.mov(assm::edi, assm::ebp); |
| asm_false2.mov(assm::eax, Operand(assm::esi)); |
| |
| BasicBlockAssembler asm_true1(true1->instructions().end(), |
| &true1->instructions()); |
| asm_true1.mov(assm::esi, Immediate(1)); |
| |
| BasicBlockAssembler asm_false1(false1->instructions().end(), |
| &false1->instructions()); |
| asm_false1.mov(assm::esi, Immediate(2)); |
| asm_false1.mov(assm::edi, Immediate(2)); |
| |
| // Perform global liveness analysis. |
| liveness_.Analyze(&subgraph); |
| |
| // Validate fix-point propagation. |
| liveness_.GetStateAtEntryOf(end2, &state_); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ebp)); |
| |
| liveness_.GetStateAtEntryOf(true2, &state_); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ebp)); |
| |
| liveness_.GetStateAtEntryOf(false2, &state_); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ebp)); |
| |
| liveness_.GetStateAtEntryOf(if2, &state_); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ebp)); |
| |
| liveness_.GetStateAtEntryOf(true1, &state_); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ebp)); |
| |
| liveness_.GetStateAtEntryOf(false1, &state_); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_FALSE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ebp)); |
| |
| liveness_.GetStateAtEntryOf(if1, &state_); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_FALSE(is_live(assm::ecx)); |
| EXPECT_FALSE(is_live(assm::esi)); |
| EXPECT_TRUE(is_live(assm::edi)); |
| EXPECT_FALSE(is_live(assm::ebp)); |
| } |
| |
| TEST_F(LivenessAnalysisTest, AnalyzeWithData) { |
| BasicBlockSubGraph subgraph; |
| const uint8_t raw_data[] = {0, 1, 2, 3, 4}; |
| |
| BlockDescription* block = subgraph.AddBlockDescription( |
| "b1", "b1.obj", BlockGraph::CODE_BLOCK, 7, 2, 42); |
| |
| BasicCodeBlock* bb = subgraph.AddBasicCodeBlock("bb"); |
| BasicDataBlock* data = |
| subgraph.AddBasicDataBlock("data", sizeof(raw_data), &raw_data[0]); |
| |
| block->basic_block_order.push_back(bb); |
| block->basic_block_order.push_back(data); |
| |
| BasicBlockAssembler asm_bb(bb->instructions().end(), &bb->instructions()); |
| asm_bb.mov(assm::eax, assm::ebx); |
| asm_bb.ret(); |
| |
| // Analyze the flow graph. |
| liveness_.Analyze(&subgraph); |
| |
| liveness_.GetStateAtEntryOf(bb, &state_); |
| EXPECT_FALSE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| |
| liveness_.GetStateAtEntryOf(data, &state_); |
| EXPECT_TRUE(is_live(assm::eax)); |
| EXPECT_TRUE(is_live(assm::ebx)); |
| EXPECT_TRUE(is_live(assm::esi)); |
| } |
| |
| } // namespace |
| |
| } // namespace analysis |
| } // namespace block_graph |