SMPInstr.cpp 536.76 KiB
/*
* SMPInstr.cpp - <see below>.
*
* Copyright (c) 2000, 2001, 2010 - University of Virginia
*
* This file is part of the Memory Error Detection System (MEDS) infrastructure.
* This file may be used and modified for non-commercial purposes as long as
* all copyright, permission, and nonwarranty notices are preserved.
* Redistribution is prohibited without prior written consent from the University
* of Virginia.
*
* Please contact the authors for restrictions applying to commercial use.
*
* THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Author: University of Virginia
* e-mail: jwd@virginia.com
* URL : http://www.cs.virginia.edu/
*
* Additional copyrights 2010, 2011 by Zephyr Software LLC
* e-mail: {clc,jwd}@zephyr-software.com
* URL : http://www.zephyr-software.com/
*
*/
//
// SMPInstr.cpp
//
// This module performs the instruction level analyses needed for the
// SMP project (Software Memory Protection).
//
using namespace std;
#include <string>
#include <cstring>
#include <pro.h>
#include <assert.h>
#include <ua.hpp>
#include <ida.hpp>
#include <idp.hpp>
#include <auto.hpp>
#include <bytes.hpp>
#include <funcs.hpp>
#include <intel.hpp>
#include <loader.hpp>
#include <lines.hpp>
#include <name.hpp>
#include "SMPDBInterface.h"
#include "SMPStaticAnalyzer.h"
#include "SMPDataFlowAnalysis.h"
#include "SMPInstr.h"
#include "SMPProgram.h"
#include "ProfilerInformation.h"
// Set to 1 for debugging output
#define SMP_DEBUG 1
#define SMP_DEBUG2 0 // verbose
#define SMP_DEBUG_XOR 0
#define SMP_DEBUG_BUILD_RTL 1 // should be left on, serious errors!
#define SMP_VERBOSE_DEBUG_BUILD_RTL 0
#define SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE 0
#define SMP_VERBOSE_DEBUG_INFER_TYPES 0
#define SMP_VERBOSE_DUMP 0
#define SMP_VERBOSE_FIND_POINTERS 0
#define STARS_DUMP_FG_INFO 1 // show signedness of DEFs and USEs in dump
#define SMP_CALL_TRASHES_REGS 1 // Add DEFs of caller-saved regs to CALL instructions
#define SMP_BASEREG_POINTER_TYPE 1 // Initialize Base Register USEs to type POINTER?
#define SMP_OPTIMIZE_ADD_TO_NUMERIC 0 // optimizing annotation type -5
#define SMP_IDENTIFY_POINTER_ADDRESS_REG 0 // optimizing annotation POINTER
#define SMP_CHILDACCESS_ALL_CODE 0 // CHILDACCESS annotations for all funcs, or just analyzed funcs?
#define SPECIAL_CASE_CARRY_BORROW 0 // Treat sbb/adc different from sub/add annotations?
#define SMP_BUILD_SPECIAL_ADC_SBB_RTL 0 // Explicit RTL subtree for carry flag?
#define SMP_AGGRESSIVE_TYPE_INFERENCE 1 // Shorten iterations by quick propagation in InferOperatorType()
#define SMP_ANNOTATE_ALL_MEMORY_OPERANDS 0 // Info annotation for all memory read and write operands?
#define SMP_AGGRESSIVE_SIGN_TRANSFER 1 // More transfer of signedness across instructions
#define STARS_NO_SHIFT_SIGNEDNESS_IN_SCALEFACTOR 1 // unsigned left shift for scale factors is really unknownsign multiply
// Make the CF_CHG1 .. CF_CHG6 and CF_USE1..CF_USE6 macros more usable
// by allowing us to pick them up with an array index.
static ulong DefMacros[UA_MAXOP] = {CF_CHG1, CF_CHG2, CF_CHG3, CF_CHG4, CF_CHG5, CF_CHG6};
static ulong UseMacros[UA_MAXOP] = {CF_USE1, CF_USE2, CF_USE3, CF_USE4, CF_USE5, CF_USE6};
// Text to be printed in each optimizing annotation explaining why
// the annotation was emitted.
static const char *OptExplanation[LAST_TYPE_CATEGORY + 1] =
{ "NoOpt", "NoMetaUpdate", "AlwaysNUM", "NUMVia2ndSrcIMMEDNUM",
"Always1stSrc", "1stSrcVia2ndSrcIMMEDNUM", "AlwaysPtr",
"AlwaysNUM", "AlwaysNUM", "NUMViaFPRegDest", "NumericSources",
"StackMemoryTracking", "NumericSources", "NumericMemDest",
"NeverMemDest", "SafeIfNoIndexing"
};
static const char *OperatorText[LAST_SMP_OPERATOR + 1] =
{ "SMP_NULL_OPERATOR", "SMP_CALL", "SMP_INPUT", "SMP_OUTPUT", "SMP_ADDRESS_OF",
"SMP_U_LEFT_SHIFT", "SMP_S_LEFT_SHIFT", "SMP_U_RIGHT_SHIFT", "SMP_S_RIGHT_SHIFT",
"SMP_ROTATE_LEFT", "SMP_ROTATE_LEFT_CARRY", "SMP_ROTATE_RIGHT", "SMP_ROTATE_RIGHT_CARRY",
"SMP_DECREMENT", "SMP_INCREMENT",
"SMP_ADD", "SMP_ADD_CARRY", "SMP_SUBTRACT", "SMP_SUBTRACT_BORROW", "SMP_U_MULTIPLY",
"SMP_S_MULTIPLY", "SMP_U_DIVIDE", "SMP_S_DIVIDE", "SMP_U_REMAINDER",
"SMP_SIGN_EXTEND", "SMP_ZERO_EXTEND", "SMP_ASSIGN", "SMP_BITWISE_AND",
"SMP_BITWISE_OR", "SMP_BITWISE_NOT", "SMP_BITWISE_XOR", "SMP_BITWISE_AND_NOT", "SMP_NEGATE",
"SMP_S_COMPARE", "SMP_U_COMPARE", "SMP_GENERAL_COMPARE", "SMP_LESS_THAN", "SMP_GREATER_THAN",
"SMP_LESS_EQUAL", "SMP_GREATER_EQUAL", "SMP_EQUAL", "SMP_NOT_EQUAL",
"SMP_LOGICAL_AND", "SMP_LOGICAL_OR", "SMP_UNARY_NUMERIC_OPERATION",
"SMP_BINARY_NUMERIC_OPERATION", "SMP_SYSTEM_OPERATION",
"SMP_UNARY_FLOATING_ARITHMETIC", "SMP_BINARY_FLOATING_ARITHMETIC",
"SMP_REVERSE_SHIFT_U", "SMP_SHUFFLE", "SMP_COMPARE_EQ_AND_SET",
"SMP_COMPARE_NE_AND_SET", "SMP_COMPARE_GT_AND_SET",
"SMP_COMPARE_GE_AND_SET", "SMP_COMPARE_LT_AND_SET", "SMP_COMPARE_LE_AND_SET",
"SMP_PACK_SIGNED", "SMP_PACK_UNSIGNED",
"SMP_AVERAGE_UNSIGNED", "SMP_MULTIPLY_AND_ADD", "SMP_SUM_OF_DIFFS",
"SMP_MAX_S", "SMP_MAX_U", "SMP_MIN_S", "SMP_MIN_U", "SMP_ABS_VALUE",
"SMP_CONVERT_INT_TO_FP", "SMP_CONVERT_FP_TO_INT", "SMP_CREATE_MASK",
"SMP_INTERLEAVE", "SMP_CONCATENATE", "SMP_EXTRACT_ZERO_EXTEND",
"SMP_ENCRYPTION_OPERATION", "SMP_SIGNAL"
};
#if 0
// Does the CurrOperator definitely indicate a signed or unsigned operation?
bool OperatorHasSignedness(SMPoperator CurrOperator) {
bool DetectedSignedness;
switch (CurrOperator) {
case SMP_NULL_OPERATOR:
DetectedSignedness = false;
break;
case SMP_CALL: // CALL instruction
DetectedSignedness = true;
break;
case SMP_INPUT: // input from port
case SMP_OUTPUT: // output to port
case SMP_ADDRESS_OF: // take effective address
case SMP_GENERAL_COMPARE:
DetectedSignedness = false;
break;
case SMP_U_LEFT_SHIFT: // unsigned left shift
case SMP_U_RIGHT_SHIFT: // unsigned right shift
case SMP_ROTATE_LEFT:
case SMP_ROTATE_LEFT_CARRY: // rotate left through carry
case SMP_ROTATE_RIGHT:
case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry
case SMP_U_MULTIPLY:
case SMP_U_DIVIDE:
case SMP_U_REMAINDER:
case SMP_ZERO_EXTEND:
case SMP_BITWISE_NOT: // unary operator
case SMP_BITWISE_XOR:
case SMP_BITWISE_AND_NOT:
case SMP_U_COMPARE: // unsigned compare (AND-based)
DetectedSignedness = true;
break;
case SMP_S_LEFT_SHIFT: // signed left shift
case SMP_S_RIGHT_SHIFT: // signed right shift
case SMP_S_MULTIPLY:
case SMP_S_DIVIDE:
case SMP_SIGN_EXTEND:
case SMP_NEGATE: // unary negation
case SMP_S_COMPARE: // signed compare (subtraction-based)
case SMP_LESS_THAN: // boolean test operators
case SMP_GREATER_THAN:
case SMP_LESS_EQUAL:
case SMP_GREATER_EQUAL:
DetectedSignedness = true;
break;
case SMP_DECREMENT:
case SMP_INCREMENT:
case SMP_ADD:
case SMP_ADD_CARRY: // add with carry
case SMP_SUBTRACT:
case SMP_SUBTRACT_BORROW: // subtract with borrow
case SMP_ASSIGN:
case SMP_BITWISE_AND:
case SMP_BITWISE_OR:
case SMP_EQUAL:
case SMP_NOT_EQUAL:
case SMP_LOGICAL_AND:
case SMP_LOGICAL_OR:
case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC
case SMP_SHUFFLE: // Shuffle bytes, words, etc. within destination operation per source mask
case SMP_SIGNAL: // signal or raise exception
DetectedSignedness = false;
break;
case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
DetectedSignedness = true;
break;
case SMP_REVERSE_SHIFT_U: // Shift right operand by bit count in left operand
case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's
case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's
case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's
case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's
case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's
case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's
case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision
case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision
case SMP_AVERAGE_U: // Average of unsigned operands
case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate)
case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements
case SMP_MAX_S: // dest := signed_max(dest, src)
case SMP_MAX_U: // dest := unsigned_max(dest, src)
case SMP_MIN_S: // dest := signed_min(dest, src)
case SMP_MIN_U: // dest := unsigned_min(dest, src)
case SMP_ABSOLUTE_VALUE: // take absolute value
case SMP_CONVERT_INT_TO_FP: // convert integer to floating point
case SMP_CONVERT_FP_TO_INT: // convert floating point to integer
case SMP_INTERLEAVE: // extended-precision interleaving of bytes or words or dwords etc.; NUMERIC
case SMP_CONCATENATE: // extended-precision concatenation; NUMERIC
DetectedSignedness = true;
break;
default:
DetectedSignedness = false;
SMP_msg("ERROR: Unknown operator in OperatorHasSignedness: %d\n", CurrOperator);
break;
} // end switch on operator
return DetectedSignedness;
} // end of OperatorHasSignedness()
#endif
// *****************************************************************
// Class SMPGuard
// *****************************************************************
// Constructor
SMPGuard::SMPGuard(void) {
this->LeftOperand.type = o_void;
this->RightOperand.type = o_void;
this->GuardOp = SMP_NULL_OPERATOR;
return;
}
// Debug print
void SMPGuard::Dump(void) const {
SMP_msg("GUARD: ");
PrintOperand(this->LeftOperand);
SMP_msg(" %s ", OperatorText[this->GuardOp]);
PrintOperand(this->RightOperand);
SMP_msg(":");
return;
} // end of SMPGuard::Dump()
// *****************************************************************
// Class SMPRegTransfer
// *****************************************************************
// Constructors
SMPRegTransfer::SMPRegTransfer(void) {
this->Guard = NULL;
this->LeftOperand.type = o_void;
this->RightOperand.type = o_void;
this->RTop.oper = SMP_NULL_OPERATOR;
this->RTop.type = UNINIT;
#if SMP_TRACK_NONSPEC_OPER_TYPE
this->RTop.NonSpeculativeType = UNINIT;
#endif
this->booleans1 = 0;
this->RightRT = NULL;
this->ParentInst = NULL;
return;
}
// Destructor
SMPRegTransfer::~SMPRegTransfer() {
#if 0
SMP_msg("Destroying SMPRegTransfer.\n");
#endif
if (NULL != this->RightRT)
delete this->RightRT;
if (NULL != this->Guard)
delete this->Guard;
return;
}
// Get the left operand, and normalize it if it is a stack operand.
op_t SMPRegTransfer::GetLeftOperand(void) const {
op_t TempOp = this->LeftOperand;
// AreDefsNormalized and AreUsesNormalized should always agree, but we
// use Defs for left operands and Uses for right operands, which is not
// strictly true, but there is no difference in results.
if (this->ParentInst->AreDefsNormalized()) {
ea_t InstAddr = this->ParentInst->GetAddr();
TempOp = this->ParentInst->GetBlock()->GetFunc()->GetNormalizedOperand(InstAddr, TempOp);
}
return TempOp;
} // end of SMPRegTransfer::GetLeftOperand()
// Get the right operand, and normalize it if it is a stack operand.
op_t SMPRegTransfer::GetRightOperand(void) const {
op_t TempOp = this->RightOperand;
if (this->ParentInst->AreDefsNormalized()) {
ea_t InstAddr = this->ParentInst->GetAddr();
TempOp = this->ParentInst->GetBlock()->GetFunc()->GetNormalizedOperand(InstAddr, TempOp);
}
return TempOp;
} // end of SMPRegTransfer::GetRightOperand()
// Does RTL subtract a non-immediate value from the stack pointer?
bool SMPRegTransfer::IsAllocaRTL(void) {
bool AllocaFound = false;
// Search for the pattern: stack_pointer := stack_pointer minus non-immediate
if ((SMP_ASSIGN == this->GetOperator()) && (this->HasRightSubTree())) {
op_t DefOp = this->GetLeftOperand();
if (DefOp.is_reg(MD_STACK_POINTER_REG)) {
// We have the code pattern stack_pointer := ...
SMPRegTransfer *RightRT = this->GetRightTree();
SMPoperator RightOperator = RightRT->GetOperator();
op_t RightDefOp = RightRT->GetLeftOperand();
if ((RightDefOp.is_reg(MD_STACK_POINTER_REG)) && (SMP_SUBTRACT == RightOperator)) {
// We have the code pattern stack_pointer := stack_pointer minus ...
if (RightRT->HasRightSubTree()) {
AllocaFound = true; // not an immediate rightop, whatever it is
}
else {
op_t RightUseOp = RightRT->GetRightOperand();
if (o_imm != RightUseOp.type) {
AllocaFound = true;
}
}
}
}
}
return AllocaFound;
} // end of SMPRegTransfer::IsAllocaRTL()
// Compute operand-dependent change in stack pointer value.
sval_t SMPRegTransfer::ComputeStackPointerAlteration(bool IsLeaveInstr, sval_t IncomingDelta, sval_t FramePtrDelta) {
sval_t delta = 0;
// Search for the pattern: stack_pointer := ...
if (SMP_ASSIGN == this->GetOperator()) {
op_t DefOp = this->GetLeftOperand();
if (DefOp.is_reg(MD_STACK_POINTER_REG)) {
// We have the code pattern stack_pointer := ...
// We expect to find an overall RT structure of:
// stack_pointer := stack_pointer binaryoperator constant
// If the binaryoperator is addition or subtraction, then
// we use the constant to determine our return value.
// If the binaryoperator is a bitwise AND, then we return
// a special code. Delta is unknown but can probably be treated
// as zero, assuming that no stack-pointer-relative accesses to
// the stack region above the current stack pointer occur later.
// If we have a non-constant operand, we return an error code
// to indicate that we cannot compute the delta.
// If we don't have a binary operator, we return an error code,
// unless we find a stack frame deallocation: esp := ebp
if (!this->HasRightSubTree()) {
op_t UseOp = this->GetRightOperand();
if (UseOp.is_reg(MD_FRAME_POINTER_REG)) { // !!!!****!!!! Should validate that frame pointer reg is used as frame pointer
// Found esp := ebp (deallocation of stack frame)
delta = FramePtrDelta - IncomingDelta;
assert(delta >= 0); // e.g. -4 - -12 => +8 for an 8-byte frame
}
else {
// !!!!****!!!!**** Need to look up deltas for registers that
// were used to hold a copy of the stack pointer.
// For now, just consider it an error.
delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE;
}
}
else {
SMPRegTransfer *RightRT = this->GetRightTree();
SMPoperator RightOperator = RightRT->GetOperator();
op_t RightDefOp = RightRT->GetLeftOperand();
if (IsLeaveInstr) {
// Found the RT esp := ebp + 4 that adjusts the stack pointer
// for an x86 LEAVE instruction. esp := ebp is equivalent
// to esp := <esp when frame ptr was set up>, so delta becomes
// <esp delta when frame ptr was set up> - IncomingDelta + 4.
assert(SMP_ADD == RightOperator);
assert(!RightRT->HasRightSubTree());
assert(RightDefOp.is_reg(X86_FRAME_POINTER_REG));
delta = 4 + FramePtrDelta - IncomingDelta;
}
else if (RightRT->HasRightSubTree()) {
// Not the right code pattern; unknown effect on stack pointer.
delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE;
}
else if (!RightDefOp.is_reg(MD_STACK_POINTER_REG)) {
// We have stack_pointer := something1 operator something2
// where something1 is not the stack_pointer. It might be
// the frame pointer, e.g. SP := FP-12. We can compute this
// stack delta because we know the delta when the FP was set up.
op_t RightUseOp = RightRT->GetRightOperand();
if (RightDefOp.is_reg(MD_FRAME_POINTER_REG) && (o_imm == RightUseOp.type)) {
// We have SP := FP operator immediate
delta = FramePtrDelta - IncomingDelta; // partial computation: SP := BP
assert(delta >= 0); // e.g. -4 - -12 => +8 for an 8-byte frame
if (SMP_ADD == RightOperator) {
delta += RightUseOp.value;
}
else if (SMP_SUBTRACT == RightOperator) {
delta -= RightUseOp.value;
}
else {
// Not the right code pattern; unknown effect on stack pointer.
delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE;
}
}
else {
// !!!!****!!!!**** Need to look up deltas for registers that
// were used to hold a copy of the stack pointer.
// For now, just consider it an error.
delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE;
}
}
else {
// We have stack_pointer := stack_pointer operator ...
op_t RightUseOp = RightRT->GetRightOperand();
// Check the operator
if (SMP_BITWISE_AND == RightOperator) {
delta = (sval_t) SMP_STACK_POINTER_BITWISE_AND_CODE;
}
else {
if (o_imm != RightUseOp.type) {
// Don't know how to deal with adding non-constant to stack pointer
delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE;
}
else if (SMP_ADD == RightOperator) {
delta = (sval_t) RightUseOp.value;
}
else if (SMP_SUBTRACT == RightOperator) {
delta = (0 - ((sval_t) RightUseOp.value));
}
else {
delta = (sval_t) SMP_STACK_DELTA_ERROR_CODE;
}
}
}
}
}
}
return delta;
} // end of SMPRegTransfer::ComputeStackPointerAlteration()
// Debug print
void SMPRegTransfer::Dump(void) const {
if (NULL != this->Guard)
this->Guard->Dump();
// Left operand
if (o_void != this->LeftOperand.type)
PrintOperand(this->LeftOperand);
// Then the operator
SMP_msg(" %s ", OperatorText[this->GetOperator()]);
// then the right operand or subtree
if (this->HasRightSubTree())
this->GetRightTree()->Dump();
else if (o_void != this->RightOperand.type)
PrintOperand(this->RightOperand);
return;
}
// *****************************************************************
// Class SMPRTL
// *****************************************************************
// Constructor
SMPRTL::SMPRTL() {
this->ExtraKills.clear();
this->RTCount = 0;
return;
}
// Destructor
SMPRTL::~SMPRTL() {
for (size_t index = 0; index < this->RTCount; ++index) {
delete (this->RTvector[index]);
}
this->ExtraKills.clear();
return;
}
// Get methods
SMPRegTransfer *SMPRTL::GetRT(size_t index) const {
if (index > this->RTCount)
return NULL;
else
return this->RTvector[index];
}
// Set methods
void SMPRTL::push_back(SMPRegTransfer *NewEffect) {
assert(SMP_RT_LIMIT > this->RTCount);
this->RTvector[this->RTCount] = NewEffect;
++(this->RTCount);
return;
}
// Printing methods
void SMPRTL::Dump(void) const {
size_t index;
if (0 < this->RTCount) {
SMP_msg("RTL: ");
for (index = 0; index < this->RTCount; ++index) {
this->RTvector[index]->Dump();
}
for (index = 0; index < this->ExtraKills.size(); ++index) {
SMP_msg(" KILL: ");
PrintOperand(this->ExtraKills.at(index));
}
SMP_msg("\n");
}
return;
} // end of SMPRTL::Dump()
// Accumulate stack pointer alteration total across all RTs.
sval_t SMPRTL::TotalStackPointerAlteration(bool IsLeaveInstr, sval_t IncomingDelta, sval_t FramePtrDelta) {
sval_t TotalDelta = 0;
sval_t IncrementalDelta;
for (size_t index = 0; index < this->RTCount; ++index) {
IncrementalDelta = this->RTvector[index]->ComputeStackPointerAlteration(IsLeaveInstr, IncomingDelta, FramePtrDelta);
if ((SMP_STACK_DELTA_ERROR_CODE == IncrementalDelta)
|| (SMP_STACK_POINTER_BITWISE_AND_CODE == IncrementalDelta)) {
TotalDelta = IncrementalDelta; // pass code back
break; // exit loop and return coded value
}
else {
TotalDelta += IncrementalDelta;
}
}
return TotalDelta;
} // end of SMPRTL::TotalStackPointerAlteration()
// *****************************************************************
// Class SMPInstr
// *****************************************************************
// Constructor for instruction.
SMPInstr::SMPInstr(ea_t addr) {
this->SMPcmd.size = 0;
this->address = addr;
#if 0
this->ResetGoodRTL();
this->ResetJumpTarget();
this->ResetBlockTerm();
this->ResetTailCall();
this->ResetCondTailCall();
this->ResetCallUsedAsJump();
this->ResetDirectRecursiveCall();
this->ResetInterrupt();
#else
this->booleans1 = 0;
#endif
#if 0
this->ResetNop();
this->ResetRegClearIdiom();
this->ResetDefsFlags();
this->ResetUsesFlags();
this->ResetFarBranchComputed();
this->ResetBranchesToFarChunk();
this->ResetIndirectMemWrite();
this->ResetIndirectMemRead();
#else
this->booleans2 = 0;
#endif
#if 0
this->ResetLoadFromStack();
this->ResetMultiplicationBitsDiscarded();
this->ResetTypeInferenceComplete();
this->ResetCategoryInferenceComplete();
this->ResetDEFsTyped();
this->ResetUSEsTyped();
#else
this->booleans3 = 0;
#endif
this->booleans4 = 0;
this->CallTarget = BADADDR;
this->FarBranchTarget = BADADDR;
this->AddSubSourceType = UNINIT;
this->AddSubUseType = UNINIT;
this->AddSubSourceOp = InitOp;
this->AddSubUseOp = InitOp;
this->DEFMemOp = InitOp;
this->USEMemOp = InitOp;
this->MoveSource = InitOp;
this->BasicBlock = NULL;
this->features = 0;
this->StackPtrOffset = 0;
this->type = DEFAULT;
this->OptType = 0;
this->Defs.clear();
this->Uses.clear();
return;
}
// Destructor.
SMPInstr::~SMPInstr() {
this->Defs.clear();
this->Uses.clear();
return;
}
char *SMPInstr::GetDisasm(void) const {
return DisAsmText.GetDisAsm(this->GetAddr());
}
// Is the instruction the type that terminates a basic block?
bool SMPInstr::IsBasicBlockTerminator() const {
return ((type == JUMP) || (type == COND_BRANCH)
|| (type == INDIR_JUMP) || (type == RETURN));
}
// Get non-flags DEF, usually for arithmetic opcode.
set<DefOrUse, LessDefUse>::iterator SMPInstr::GetFirstNonFlagsDef(void) {
set<DefOrUse, LessDefUse>::iterator DefIter;
op_t DefOp;
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
DefOp = DefIter->GetOp();
if (!((o_reg == DefOp.type) && DefOp.is_reg(MD_FLAGS_REG)))
break; // found a non-flags-reg DEF.
}
return DefIter;
}
// Is the destination operand a memory reference?
bool SMPInstr::HasDestMemoryOperand(void) {
return (o_void != this->DEFMemOp.type);
} // end of SMPInstr::HasDestMemoryOperand()
// Is a source operand a memory reference?
bool SMPInstr::HasSourceMemoryOperand(void) {
return (o_void != this->USEMemOp.type);
} // end of SMPInstr::HasSourceMemoryOperand()
// Get the first memory operand in the DEF list.
op_t SMPInstr::MDGetMemDefOp(void) const {
return this->DEFMemOp; // cached value
} // end of SMPInstr::MDGetMemDefOp()
// Get the first memory operand in the USE list.
op_t SMPInstr::MDGetMemUseOp(void) const {
return this->USEMemOp; // cached value
} // end of SMPInstr::MDGetMemUseOp()
// return original Lea instruction [pseudo-]memory operand.
op_t SMPInstr::GetLeaMemUseOp(void) {
if (this->BasicBlock == NULL) {
return InitOp;
}
else {
map<ea_t, op_t>::iterator MapIter = this->GetBlock()->GetFunc()->FindLeaOperand(this->GetAddr());
return MapIter->second;
}
}
// return BADADDR if not jump, target addr otherwise.
ea_t SMPInstr::GetJumpTarget(void) const {
ea_t TargetAddr = BADADDR;
if (this->HasGoodRTL()) {
// We want to find an RTL of the form: inst_ptr_reg := code_addr
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
op_t DefOp = CurrRT->GetLeftOperand();
if (DefOp.is_reg(MD_INSTRUCTION_POINTER_REG)) {
if ((SMP_ASSIGN == CurrRT->GetOperator()) && (!CurrRT->HasRightSubTree())) {
op_t UseOp = CurrRT->GetRightOperand();
if ((o_near == UseOp.type) || (o_far == UseOp.type)) { // address
TargetAddr = UseOp.addr;
}
}
}
}
return TargetAddr;
} // end SMPInstr::GetJumpTarget()
// Does any USE have type NEGATEDPTR?
bool SMPInstr::HasNegatedPtrUSE(void) {
bool UseFound = false;
set<DefOrUse, LessDefUse>::iterator UseIter;
SMPOperandType UseType;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseType = UseIter->GetType();
if (IsEqType(UseType, NEGATEDPTR)) {
UseFound = true;
break;
}
}
return UseFound;
} // end of SMPInstr::HasNegatedPtrUSE()
// Detect indirect memory DEFs or USEs
void SMPInstr::AnalyzeIndirectRefs(bool UseFP) {
op_t DefMemOp = this->MDGetMemDefOp();
op_t UseMemOp = this->MDGetMemUseOp();
if (o_void != DefMemOp.type) {
// Found a memory DEF. Is it indirect?
if (MDIsIndirectMemoryOpnd(DefMemOp, UseFP)) {
this->SetIndirectMemWrite();
}
}
if (o_void != UseMemOp.type) {
// Found a memory USE. Is it indirect?
if (MDIsIndirectMemoryOpnd(UseMemOp, UseFP)) {
this->SetIndirectMemRead();
}
}
return;
} // end of SMPInstr::AnalyzeIndirectRefs()
set<DefOrUse, LessDefUse>::iterator SMPInstr::GetPointerAddressReg(op_t MemOp) {
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t displacement;
set<DefOrUse, LessDefUse>::iterator PtrIter;
if ((NULL == this->BasicBlock) || (NULL == this->BasicBlock->GetFunc())) {
SMP_msg("ERROR: NULL member pointers in SMPInstr::GetPointerAddressReg() at %x \n",
this->address);
return this->GetLastUse();
}
bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
MDExtractAddressFields(MemOp, BaseReg, IndexReg, ScaleFactor,
displacement);
if ((R_none != BaseReg) && (R_sp != BaseReg)
&& (!(UseFP && (R_bp == BaseReg)))) {
op_t BaseOp = InitOp;
BaseOp.type = o_reg;
BaseOp.reg = (ushort) BaseReg;
PtrIter = this->FindUse(BaseOp);
assert(PtrIter != this->GetLastUse());
if (IsDataPtr(PtrIter->GetType())) {
return PtrIter;
}
}
if ((R_none != IndexReg) && (R_sp != IndexReg)
&& (!(UseFP && (R_bp == IndexReg)))) {
op_t IndexOp = InitOp;
IndexOp.type = o_reg;
IndexOp.reg = (ushort) IndexReg;
PtrIter = this->FindUse(IndexOp);
assert(PtrIter != this->GetLastUse());
if (IsDataPtr(PtrIter->GetType())) {
return PtrIter;
}
}
PtrIter = this->GetLastUse();
return PtrIter;
} // end of SMPInstr::GetPointerAddressReg()
// Does the instruction whose flags are in F have a numeric type
// as the second source operand?
// NOTE: We can only analyze immediate values now. When data flow analyses are implemented,
// we will be able to analyze many non-immediate operands.
#define IMMEDNUM_LOWER -8191
#define IMMEDNUM_UPPER 8191
bool SMPInstr::IsSecondSrcOperandNumeric(flags_t F) const {
bool SecondOpImm = (this->SMPcmd.Operands[1].type == o_imm);
uval_t TempImm;
if (SecondOpImm) {
TempImm = this->SMPcmd.Operands[1].value;
}
return (SecondOpImm && IsImmedNumeric(TempImm));
} // end of SMPInstr::IsSecondSrcOperandNumeric()
// Determine the type of the USE-only operand for add and subtract
// instructions. If it is NUMERIC or PROF_NUMERIC, an optimizing
// annotation will result.
// As a byproduct, find the type of the USE/DEF operand as well.
void SMPInstr::SetAddSubSourceType(void) {
// Walk the RTL and find the operands we care about.
// The RTL should look like: opnd1 := (opnd1 op opnd2), where op is
// and add or subtract operator. Within the parentheses, the type
// of opnd1 is our AddSubUseType and opnd1 is our AddSubUseOp, while
// the type of opnd2 is our AddSubSourceType.
if (this->RTL.GetCount() < 1)
return; // no RTL, no leave types as UNINIT.
assert(this->RTL.GetRT(0)->HasRightSubTree());
SMPRegTransfer *RightTree = this->RTL.GetRT(0)->GetRightTree();
op_t LeftOp, RightOp;
LeftOp = RightTree->GetLeftOperand(); // Use (also DEF) operand
#if SMP_BUILD_SPECIAL_ADC_SBB_RTL
if ((NN_adc != this->SMPcmd.itype) && (NN_sbb != this->SMPcmd.itype)) {
assert(!(RightTree->HasRightSubTree()));
RightOp = RightTree->GetRightOperand(); // Src (non-DEF) operand
}
else {
// Add with carry and subtract with borrow have an extra level
// to the tree RTL, e.g. for add with carry:
// opnd1 := (opnd1 + (opnd2 + carryflag))
assert(RightTree->HasRightSubTree());
RightTree = RightTree->GetRightTree();
RightOp = RightTree->GetLeftOperand();
}
#else
assert(!(RightTree->HasRightSubTree()));
RightOp = RightTree->GetRightOperand(); // Src (non-DEF) operand
#endif
set<DefOrUse, LessDefUse>::iterator UseIter, SrcIter;
SrcIter = this->FindUse(RightOp);
assert(SrcIter != this->GetLastUse());
this->AddSubSourceType = SrcIter->GetType();
this->AddSubSourceOp = RightOp;
UseIter = this->FindUse(LeftOp);
assert(UseIter != this->GetLastUse());
this->AddSubUseType = UseIter->GetType();
this->AddSubUseOp = LeftOp;
return;
} // end of SMPInstr::SetAddSubSourceType()
// Are all DEFs in the DEF set NUMERIC type?
bool SMPInstr::AllDefsNumeric(void)
{
bool AllNumeric = (this->Defs.GetSize() > 0); // false if no DEFs, true otherwise
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) {
// We ignore the stack pointer for pop instructions and consider only
// the register DEF of the pop.
if (this->MDIsPopInstr() && CurrDef->GetOp().is_reg(R_sp))
continue;
AllNumeric = (AllNumeric && IsNumeric(CurrDef->GetType()));
}
return AllNumeric;
} // end of SMPInstr::AllDefsNumeric()
// Were the types of any DEFs derived from profiler info?
bool SMPInstr::AnyDefsProfiled(void)
{
bool profd = false;
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) {
profd = (profd || IsProfDerived(CurrDef->GetType()));
}
return profd;
}
// Do all DEFs have DEF_METADATA_UNUSED status?
bool SMPInstr::AllDefMetadataUnused(void) {
bool AllUnused = (this->Defs.GetSize() > 0); // false if no DEFs, true otherwise
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) {
AllUnused = (AllUnused
&& (DEF_METADATA_UNUSED == CurrDef->GetMetadataStatus()));
}
return AllUnused;
} // end of SMPInstr::AllDefMetadataUnused()
// DEBUG print operands for Inst.
void SMPInstr::PrintOperands(void) const {
op_t Opnd;
for (int i = 0; i < UA_MAXOP; ++i) {
Opnd = SMPcmd.Operands[i];
PrintOneOperand(Opnd, this->features, i);
}
SMP_msg(" \n");
return;
} // end of SMPInstr::PrintOperands()
// Complete DEBUG printing.
void SMPInstr::Dump(void) {
SMP_msg("%x %d SMPitype: %d %s\n", this->address, this->SMPcmd.size, (int) this->type,
DisAsmText.GetDisAsm(this->GetAddr()));
#if STARS_DUMP_FG_INFO
SMP_msg("USEs: ");
if (NULL == this->GetBlock()) { // during early improvement of disasm
this->Uses.Dump();
SMP_msg("DEFs: ");
this->Defs.Dump();
}
else {
set<DefOrUse, LessDefUse>::iterator UseIter, DefIter;
op_t UseOp, DefOp;
int UseHashValue, DefHashValue, UseSSANum, DefSSANum;
unsigned short SignMiscInfo, SignMask;
bool LocalName;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseIter->Dump();
UseOp = UseIter->GetOp();
if (o_reg == UseOp.type) {
LocalName = this->GetBlock()->IsLocalName(UseOp);
UseSSANum = UseIter->GetSSANum();
UseHashValue = HashGlobalNameAndSSA(UseOp, UseSSANum);
if (LocalName) {
SignMiscInfo = this->GetBlock()->GetUseSignMiscInfo(UseHashValue);
}
else {
SignMiscInfo = this->GetBlock()->GetFunc()->GetUseSignMiscInfo(UseHashValue);
}
SignMask = SignMiscInfo & FG_MASK_SIGNEDNESS_BITS;
if (SignMask == FG_MASK_SIGNED) {
SMP_msg(" Si ");
}
else if (SignMask == FG_MASK_UNSIGNED) {
SMP_msg(" Un ");
}
else if (SignMask == FG_MASK_INCONSISTENT_SIGN) {
SMP_msg(" Xs ");
}
}
SMP_msg("\n");
}
SMP_msg("DEFs: ");
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
DefIter->Dump();
DefOp = DefIter->GetOp();
if (o_reg == DefOp.type) {
LocalName = this->GetBlock()->IsLocalName(DefOp);
DefSSANum = DefIter->GetSSANum();
DefHashValue = HashGlobalNameAndSSA(DefOp, DefSSANum);
if (LocalName) {
SignMiscInfo = this->GetBlock()->GetDefSignMiscInfo(DefHashValue);
}
else {
SignMiscInfo = this->GetBlock()->GetFunc()->GetDefSignMiscInfo(DefHashValue);
}
SignMask = SignMiscInfo & FG_MASK_SIGNEDNESS_BITS;
if (SignMask == FG_MASK_SIGNED) {
SMP_msg(" Si ");
}
else if (SignMask == FG_MASK_UNSIGNED) {
SMP_msg(" Un ");
}
else if (SignMask == FG_MASK_INCONSISTENT_SIGN) {
SMP_msg(" Xs ");
}
}
SMP_msg("\n");
}
}
#else
SMP_msg("USEs: ");
this->Uses.Dump();
SMP_msg("DEFs: ");
this->Defs.Dump();
#endif
this->RTL.Dump();
#if SMP_VERBOSE_DUMP
this->PrintOperands();
#endif
SMP_msg("\n");
return;
} // end of SMPInstr::Dump()
// Print out the destination operand list for the instruction, given
// the OptCategory for the instruction as a hint.
char * SMPInstr::DestString(int OptType) {
static char DestList[MAXSTR];
int RegDestCount = 0;
DestList[0] = 'Z'; // Make sure there are no leftovers from last call
DestList[1] = 'Z';
DestList[2] = '\0';
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) {
op_t DefOpnd = CurrDef->GetOp();
if (DefOpnd.is_reg(X86_FLAGS_REG)) // don't print flags as a destination
continue;
// We want to ignore the stack pointer DEF for pops and just include
// the register DEF for the pop.
if (DefOpnd.is_reg(R_sp) && this->MDIsPopInstr())
continue;
if (o_reg == DefOpnd.type) {
ushort DestReg = DefOpnd.reg;
if (0 == RegDestCount) {
SMP_strncpy(DestList, RegNames[DestReg], 1 + strlen(RegNames[DestReg]));
}
else {
SMP_strncat(DestList, " ", MAXSTR);
SMP_strncat(DestList, RegNames[DestReg], MAXSTR);
}
++RegDestCount;
}
}
if (0 >= RegDestCount) {
SMP_msg("WARNING: No destination registers: %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
}
else {
SMP_strncat(DestList, " ZZ ", MAXSTR);
}
return DestList;
} // end of SMPInstr::DestString()
// print the registers that are dead right before this instruction.
void SMPInstr::PrintDeadRegs(FILE *OutputFile) {
size_t RegNum = (size_t) MD_FLAGS_REG;
// Start with the flags register.
if (this->DeadRegsBitmap[RegNum]) {
SMP_fprintf(OutputFile, "%s ", RegNames[RegNum]);
}
// Do all other registers
for (RegNum = 0; RegNum < MD_FLAGS_REG; ++RegNum) {
if (this->DeadRegsBitmap[RegNum]) {
SMP_fprintf(OutputFile, "%s ", RegNames[RegNum]);
}
}
for (RegNum = 1 + MD_FLAGS_REG; RegNum <= MD_LAST_REG_NO; ++RegNum) {
if (this->DeadRegsBitmap[RegNum]) {
SMP_fprintf(OutputFile, "%s ", RegNames[RegNum]);
}
}
// Print sequence terminator.
SMP_fprintf(OutputFile, "ZZ");
return;
} // end of SMPInstr::PrintDeadRegs()
// Equality operator for SMPInstr. Key field is address.
int SMPInstr::operator==(const SMPInstr &rhs) const {
if (this->address != rhs.GetAddr())
return 0;
else
return 1;
}
// Inequality operator for SMPInstr. Key field is address.
int SMPInstr::operator!=(const SMPInstr &rhs) const {
return (this->address != rhs.GetAddr());
}
// Less than operator for sorting SMPInstr lists. Key field is address.
int SMPInstr::operator<(const SMPInstr &rhs) const {
return (this->address < rhs.GetAddr());
}
// Less than or equal operator for sorting SMPInstr lists. Key field is address.
int SMPInstr::operator<=(const SMPInstr &rhs) const {
return (this->address <= rhs.GetAddr());
}
#define MD_FIRST_ENTER_INSTR NN_enterw
#define MD_LAST_ENTER_INSTR NN_enterq
// Is this instruction one that allocates space on the
// stack for the local variables?
bool SMPInstr::MDIsFrameAllocInstr(void) {
// The frame allocating instruction should look like:
// sub esp,48 or add esp,-64 etc.
op_t ESPOp = InitOp;
ESPOp.type = o_reg;
ESPOp.reg = R_sp;
if ((SMPcmd.itype == NN_sub) || (SMPcmd.itype == NN_add)) {
if (this->GetLastDef() != this->Defs.FindRef(ESPOp)) {
// We know that an addition or subtraction is being
// performed on the stack pointer. This should not be
// possible within the prologue except at the stack
// frame allocation instruction, so return true. We
// could be more robust in this analysis in the future. **!!**
// CAUTION: If a compiler allocates 64 bytes for locals
// and 16 bytes for outgoing arguments in a single
// instruction: sub esp,80
// you cannot insist on finding sub esp,LocSize
// To make this more robust, we are going to insist that
// an allocation of stack space is either performed by
// adding a negative immediate value, or by subtracting
// a positive immediate value. We will throw in, free of
// charge, a subtraction of a register, which is how alloca()
// usually allocates stack space.
// PHASE ORDERING: Should we use the Operands[] instead of the USE list? **!!**
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) {
if (o_imm == CurrUse->GetOp().type) {
signed long TempImm = (signed long) CurrUse->GetOp().value;
if (((0 > TempImm) && (this->SMPcmd.itype == NN_add))
|| ((0 < TempImm) && (this->SMPcmd.itype == NN_sub))) {
return true;
}
}
else if ((o_reg == CurrUse->GetOp().type)
&& (!CurrUse->GetOp().is_reg(R_sp)) // skip the ESP operand
&& (this->SMPcmd.itype == NN_sub)) { // sub esp,reg: alloca() ?
return true;
}
}
}
}
else if ((this->SMPcmd.itype >= MD_FIRST_ENTER_INSTR)
&& (this->SMPcmd.itype <= MD_LAST_ENTER_INSTR)) {
return true;
}
return false;
} // end of SMPInstr::MDIsFrameAllocInstr()
#define MD_FIRST_LEAVE_INSTR NN_leavew
#define MD_LAST_LEAVE_INSTR NN_leaveq
// Is this instruction in the epilogue the one that deallocates the local
// vars region of the stack frame?
bool SMPInstr::MDIsFrameDeallocInstr(bool UseFP, asize_t LocalVarsSize) {
// The usual compiler idiom for the prologue on x86 is to
// deallocate the local var space with: mov esp,ebp
// It could be add esp,constant. We can be tricked by
// add esp,constant when the constant is just the stack
// adjustment after a call. We will have to insist that
// the immediate operand have at least the value of
// LocalVarsSize for this second form, and that UseFP be true
// for the first form.
set<DefOrUse, LessDefUse>::iterator FirstDef = this->GetFirstDef();
set<DefOrUse, LessDefUse>::iterator FirstUse = this->GetFirstUse();
if ((SMPcmd.itype >= MD_FIRST_LEAVE_INSTR) && (SMPcmd.itype <= MD_LAST_LEAVE_INSTR))
return true;
else if (this->HasDestMemoryOperand() || this->HasSourceMemoryOperand()) {
// Don't get fooled by USE or DEF entries of EBP or ESP that come
// from memory operands, e.g. mov eax,[ebp-20]
return false;
}
else if (UseFP && (this->SMPcmd.itype == NN_mov)
&& (FirstDef->GetOp().is_reg(R_sp))
&& (FirstUse->GetOp().is_reg(R_bp)))
return true;
else if ((this->SMPcmd.itype == NN_add)
&& (FirstDef->GetOp().is_reg(R_sp))) {
set<DefOrUse, LessDefUse>::iterator SecondUse = ++FirstUse;
if (SecondUse == this->Uses.GetLastRef())
return false; // no more USEs ... strange for ADD instruction
if (SecondUse->GetOp().is_imm((uval_t) LocalVarsSize))
return true;
else if (SecondUse->GetOp().type == o_imm) {
signed long TempImm = (signed long) this->SMPcmd.Operands[1].value;
if (0 > TempImm) // adding a negative to ESP; alloc, not dealloc
return false;
else {
SMP_msg("Used imprecise LocalVarsSize to find dealloc instr.\n");
return true;
}
}
else
return false;
}
else
return false;
} // end of SMPInstr::MDIsFrameDeallocInstr()
// Is instruction a no-op? There are 1-byte, 2-byte, etc., versions of no-ops.
bool SMPInstr::MDIsNop(void) const {
bool IsNop = false;
ushort opcode = this->SMPcmd.itype;
// NOTE: More examples have arisen, e.g. xchg reg with itself. !!!!!!
if (NN_nop == opcode)
IsNop = true;
else if ((NN_mov == opcode) || (NN_xchg == opcode)) {
if ((o_reg == this->SMPcmd.Operands[0].type)
&& this->SMPcmd.Operands[1].is_reg(this->SMPcmd.Operands[0].reg)) {
// We have a register to register move with source == destination,
// or a register exchanged with itself.
IsNop = true;
}
}
else if (NN_lea == opcode) {
if ((o_reg == this->SMPcmd.Operands[0].type)
&& (o_displ == this->SMPcmd.Operands[1].type)
&& (0 == this->SMPcmd.Operands[1].addr)) {
// We are looking for 6-byte no-ops like lea esi,[esi+0]
ushort destreg = this->SMPcmd.Operands[0].reg;
if ((this->SMPcmd.Operands[1].hasSIB)
&& (destreg == (ushort) sib_base(this->SMPcmd.Operands[1]))
&& (R_sp == sib_index(this->SMPcmd.Operands[1]))) {
// R_sp signifies no SIB index register. So, we have
// lea reg,[reg+0] with reg being the same in both place,
// once as Operands[0] and once as the base reg in Operands[1].
IsNop = true;
}
else if (destreg == this->SMPcmd.Operands[1].reg) {
IsNop = true;
}
}
}
return IsNop;
} // end of SMPInstr::MDIsNop()
// Opcode always produces an UNSIGNED DEF.
bool SMPInstr::MDAlwaysUnsignedDEF(void) const {
ushort opcode = this->SMPcmd.itype;
return ((opcode == NN_bsf) || (opcode == NN_bsr) || (opcode == NN_div)
|| (opcode == NN_lahf) || (opcode == NN_lar) || (opcode == NN_lgs)
|| (opcode == NN_lss) || (opcode == NN_lds) || (opcode == NN_les)
|| (opcode == NN_lfs) || (opcode == NN_lsl) || (opcode == NN_movzx)
|| (opcode == NN_rcl) || (opcode == NN_rcr) || (opcode == NN_rol)
|| (opcode == NN_ror) || (opcode == NN_shl) || (opcode == NN_shr)
|| ((opcode >= NN_seta) && (opcode <= NN_setz)) || (opcode == NN_cpuid)
|| (opcode == NN_rdtsc) || (opcode == NN_rdpmc) || (opcode == NN_fstsw)
|| (opcode == NN_setalc) || (opcode == NN_packuswb) || (opcode == NN_paddusb)
|| (opcode == NN_paddusw) || (opcode == NN_psllw) || (opcode == NN_pslld)
|| (opcode == NN_psllq) || (opcode == NN_psrlw) || (opcode == NN_psrld)
|| (opcode == NN_psrlq) || (opcode == NN_psubusb) || (opcode == NN_psubusw)
|| (opcode == NN_pxor) || (opcode == NN_pavgusb) || (opcode == NN_pavgb)
|| (opcode == NN_pavgw) || (opcode == NN_pextrw) || (opcode == NN_pmaxub)
|| ((opcode >= NN_pminub) && (opcode <= NN_psadbw))
|| (opcode == NN_movmskpd) || (opcode == NN_pmuludq) || (opcode == NN_pslldq)
|| (opcode == NN_psrldq) || ((opcode >= NN_pabsb) && (opcode <= NN_pabsd))
|| (opcode == NN_rdtscp) || (opcode == NN_mpsadbw) || (opcode == NN_packusdw)
|| ((opcode >= NN_pcmpeqq) && (opcode <= NN_phminposuw)) || (opcode == NN_pmaxud)
|| (opcode == NN_pmaxuw) || (opcode == NN_pminud) || (opcode == NN_pminuw)
|| ((opcode >= NN_pmovzxbw) && (opcode <= NN_pmovzxdq)) || ((opcode >= NN_crc32)
&& (opcode <= NN_pcmpistrm)) || (opcode == NN_popcnt) || (opcode == NN_lzcnt)
|| ((opcode >= NN_aesenc) && (opcode <= NN_aeskeygenassist)));
} // end of SMPInstr::MDAlwaysUnsignedDEF()
// Opcode always produces a SIGNED DEF.
bool SMPInstr::MDAlwaysSignedDEF(void) const {
ushort opcode = this->SMPcmd.itype;
return ((opcode == NN_cbw) || (opcode == NN_cwde) || (opcode == NN_cdqe)
|| (opcode == NN_cwd) || (opcode == NN_cdq) || (opcode == NN_cqo)
|| (opcode == NN_idiv) || (opcode == NN_movsx) || (opcode == NN_neg)
|| (opcode == NN_sal) || (opcode == NN_sar) || (opcode == NN_fist)
|| (opcode == NN_fistp) || (opcode == NN_fbstp) || (opcode == NN_packsswb)
|| (opcode == NN_packssdw) || (opcode == NN_paddsb) || (opcode == NN_paddsw)
|| (opcode == NN_pmaddwd) || (opcode == NN_pmulhw) || (opcode == NN_pmullw)
|| (opcode == NN_psraw) || (opcode == NN_psrad) || (opcode == NN_psubsb)
|| (opcode == NN_psubsw) || (opcode == NN_pfadd) || (opcode == NN_pfsub)
|| (opcode == NN_pfsubr) || (opcode == NN_pfacc) || (opcode == NN_pfmin)
|| (opcode == NN_pfmax) || (opcode == NN_pf2id) || (opcode == NN_pfrcp)
|| (opcode == NN_pfadd) || (opcode == NN_pfrsqrt) || (opcode == NN_pfmul)
|| (opcode == NN_pfrcpit1) || (opcode == NN_pfrsqit1) || (opcode == NN_pfrcpit2)
|| (opcode == NN_pmulhrw) || ((opcode >= NN_addps) && (opcode <= NN_andps))
|| ((opcode >= NN_cvtpi2ps) && (opcode <= NN_divss)) || ((opcode >= NN_maxps)
&& (opcode <= NN_movlps)) || ((opcode >= NN_movss) && (opcode <= NN_sqrtss))
|| (opcode == NN_subps) || (opcode == NN_subss) || (opcode == NN_unpckhps)
|| (opcode == NN_unpcklps) || (opcode == NN_pmaxsw) || (opcode == NN_pminsw)
|| (opcode == NN_movntps) || (opcode == NN_pf2iw) || (opcode == NN_pfnacc)
|| (opcode == NN_pfpnacc) || (opcode == NN_pi2fw) || ((opcode >= NN_addpd)
&& (opcode <= NN_andpd)) || ((opcode >= NN_cvtdq2pd) && (opcode <= NN_divsd))
|| (opcode == NN_maxpd) || (opcode == NN_maxsd) || ((opcode >= NN_minpd)
&& (opcode <= NN_movapd)) || (opcode == NN_movhpd) || (opcode == NN_movlpd)
|| (opcode == NN_movntpd) || ((opcode >= NN_movsd) && (opcode <= NN_orpd))
|| ((opcode >= NN_sqrtpd) && (opcode <= NN_subsd)) && ((opcode >= NN_unpckhpd)
&& (opcode <= NN_xorpd)) || ((opcode >= NN_movddup) && (opcode <= NN_movsxd))
|| ((opcode >= NN_addsubpd) && (opcode <= NN_hsubps)) || (opcode == NN_fisttp)
|| ((opcode >= NN_psignb) && (opcode <= NN_psignd)) || ((opcode >= NN_pmulhrsw)
&& (opcode <= NN_phsubd)) || (opcode == NN_pfrcpv) || (opcode == NN_pfrsqrtv)
|| ((opcode >= NN_blendpd) && (opcode <= NN_insertps)) || (opcode == NN_pmaxsb)
|| (opcode == NN_pmaxsd) || (opcode == NN_pminsb) || (opcode == NN_pminsd)
|| ((opcode >= NN_pmovsxbw) && (opcode <= NN_pmovsxdq)) || (opcode == NN_pmuldq)
|| (opcode == NN_pmulld) || ((opcode >= NN_roundpd) && (opcode <= NN_roundss))
|| (opcode == NN_movntsd) || (opcode == NN_movntss));
} // end of SMPInstr::MDAlwaysSignedDEF()
bool SMPInstr::MDIsAddition(void) const {
unsigned short opcode = this->SMPcmd.itype;
bool FoundAddition = ((NN_adc == opcode) || (NN_add == opcode));
if (this->MDIsLoadEffectiveAddressInstr()) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
if (CurrRT->HasRightSubTree()) {
CurrRT = CurrRT->GetRightTree();
FoundAddition = (SMP_ADD == CurrRT->GetOperator());
}
}
return FoundAddition;
}
// Is non-multiply arithmetic instruction that can possibly overflow?
bool SMPInstr::MDIsOverflowingOpcode(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_adc == opcode) || (NN_add == opcode) || (NN_inc == opcode)
|| (NN_neg == opcode) || (NN_xadd == opcode));
}
// Is non-multiply arithmetic instruction that can possibly underflow?
bool SMPInstr::MDIsUnderflowingOpcode(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_dec == opcode) || (NN_sbb == opcode) || (NN_sub == opcode));
}
// Is potentially benign overflow instruction?
bool SMPInstr::MDIsMaybeBenignOverflowOpcode(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_adc == opcode) || (NN_add == opcode));
}
// Is potentially benign underflow instruction?
bool SMPInstr::MDIsMaybeBenignUnderflowOpcode(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_neg == opcode) || (NN_sbb == opcode) || (NN_sub == opcode));
}
// Is definitely benign underflow instruction?
// NOTE: Overlaps with MDIsMaybeBenignUnderflowOpcode(), so call this one first.
bool SMPInstr::MDIsDefiniteBenignUnderflowOpcode(int &IdiomCode) {
unsigned short opcode = this->SMPcmd.itype;
// gcc use: sbb edx,edx as a tricky way to get all zeroes or all ones into edx.
// (Some sort of saturation? Often treated as 0 or -1, i.e. it becomes SIGNED
// even if it used to be UNSIGNED.)
// The "underflow" on the subtraction is irrelevant and benign.
bool benign = ((NN_sbb == opcode) && (this->SubtractsFromItself()));
if (benign) {
IdiomCode = 4;
}
return benign;
}
// Does a subtraction operator get applied to same left and right operands?
bool SMPInstr::SubtractsFromItself(void) {
bool SelfSubtract = false;
size_t RTLCount = this->RTL.GetCount();
for (size_t index = 0; index < RTLCount; ++index) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(index);
if ((CurrRT != NULL) && (CurrRT->HasRightSubTree())) {
CurrRT = CurrRT->GetRightTree();
SMPoperator CurrOp = CurrRT->GetOperator();
if ((SMP_SUBTRACT_BORROW == CurrOp) || (SMP_SUBTRACT == CurrOp)) {
if (!(CurrRT->HasRightSubTree())) {
// NOTE: Must change this code when we build more precise SMP_SUBTRACT_BORROW RTL.
op_t LeftOp = CurrRT->GetLeftOperand();
op_t RightOp = CurrRT->GetRightOperand();
SelfSubtract = IsEqOp(RightOp, LeftOp);
}
break;
}
}
}
return SelfSubtract;
} // end of SMPInstr::SubtractsFromItself()
// Does instruction subtract an immediate value that is often used in ASCII computations,
// such as the ASCII code for '0', 'a', or 'A' ?
bool SMPInstr::SubtractsImmedASCII(void) {
return (this->MDIsMaybeBenignUnderflowOpcode() && (o_imm == this->AddSubSourceOp.type)
&& (('0' == this->AddSubSourceOp.value) || ('a' == this->AddSubSourceOp.value)
|| ('A' == this->AddSubSourceOp.value)));
}
// Does instruction compare a location to an immediate value that is often used in ASCII computations,
// such as the ASCII code for '0' or '9', 'a' or 'A', 'z' or 'Z', or carriage return?
#define SMP_ASCII_CARRIAGE_RETURN 13
bool SMPInstr::MDComparesImmedASCII(void) {
bool ComparesToASCII = false;
if (this->SMPcmd.itype == NN_cmp) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
if ((CurrRT != NULL) && (CurrRT->HasRightSubTree())) {
CurrRT = CurrRT->GetRightTree();
if (!(CurrRT->HasRightSubTree())) {
op_t LeftOp = CurrRT->GetLeftOperand();
op_t RightOp = CurrRT->GetRightOperand();
if ((o_imm == RightOp.type) && ((SMP_ASCII_CARRIAGE_RETURN == RightOp.value)
|| ('0' == RightOp.value) || ('9' == RightOp.value)
|| ('a' == RightOp.value) || ('z' == RightOp.value)
|| ('A' == RightOp.value) || ('Z' == RightOp.value))) {
ComparesToASCII = true;
}
}
}
}
return ComparesToASCII;
} // end of SMPInstr::MDComparesImmedASCII()
// Multiply by large constant; overflow is probably intentional.
#define STARS_LARGE_UNSIGNED_MUL_CONSTANT_THRESHOLD 0x20000000
#define STARS_LARGE_SIGNED_MUL_CONSTANT_THRESHOLD 0x10000000
#define STARS_SMALL_SIGNED_MUL_CONSTANT_THRESHOLD ((int)(-STARS_LARGE_SIGNED_MUL_CONSTANT_THRESHOLD))
bool SMPInstr::IsMultiplyByLargeConstant(uval_t &ConstValue, unsigned short SignMask) {
bool LargeConstFound = false;
if (this->MDIsMultiply() && (SignMask != FG_MASK_INCONSISTENT_SIGN) && (SignMask != 0)) {
set<DefOrUse, LessDefUse>::iterator UseIter;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
if (this->FindConstantValue(UseIter, ConstValue)) {
if (FG_MASK_UNSIGNED == SignMask) {
if (((uval_t) STARS_LARGE_UNSIGNED_MUL_CONSTANT_THRESHOLD) <= ConstValue) {
LargeConstFound = true;
break;
}
}
else if (FG_MASK_SIGNED == SignMask) {
int SignedConstValue = (int) ConstValue;
if ( (((int) STARS_LARGE_SIGNED_MUL_CONSTANT_THRESHOLD) <= SignedConstValue)
|| (((int) STARS_SMALL_SIGNED_MUL_CONSTANT_THRESHOLD) >= SignedConstValue)) {
LargeConstFound = true;
break;
}
}
}
}
}
return LargeConstFound;
} // end of SMPInstr::IsMultiplyByLargeConstant()
// Subtraction of large constant; underflow is probably intentional.
#define STARS_LARGE_UNSIGNED_SUB_CONSTANT_THRESHOLD 0x80000000
#define STARS_LARGE_SIGNED_SUB_CONSTANT_THRESHOLD 0x40000000
#define STARS_SMALL_SIGNED_SUB_CONSTANT_THRESHOLD ((int)(-STARS_LARGE_SIGNED_SUB_CONSTANT_THRESHOLD))
bool SMPInstr::IsSubtractionOfLargeConstant(uval_t &ConstValue, unsigned short SignMask) {
bool LargeConstFound = false;
if (this->MDIsUnderflowingOpcode() && (SignMask != FG_MASK_INCONSISTENT_SIGN) && (SignMask != 0)) {
op_t SubtrahendOp = this->AddSubSourceOp;
if (o_void != SubtrahendOp.type) {
set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(SubtrahendOp);
if (this->FindConstantValue(UseIter, ConstValue)) {
if (FG_MASK_UNSIGNED == SignMask) {
if (((uval_t) STARS_LARGE_UNSIGNED_SUB_CONSTANT_THRESHOLD) <= ConstValue) {
LargeConstFound = true;
}
}
else if (FG_MASK_SIGNED == SignMask) {
int SignedConstValue = (int) ConstValue;
if ( (((int) STARS_LARGE_SIGNED_SUB_CONSTANT_THRESHOLD) <= SignedConstValue)
|| (((int) STARS_SMALL_SIGNED_SUB_CONSTANT_THRESHOLD) >= SignedConstValue)) {
LargeConstFound = true;
}
}
}
}
}
return LargeConstFound;
} // end of SMPInstr::IsSubtractionOfLargeConstant()
// Subtraction of large constant; underflow is probably intentional.
#define STARS_LARGE_UNSIGNED_ADD_CONSTANT_THRESHOLD 0x80000000
#define STARS_LARGE_SIGNED_ADD_CONSTANT_THRESHOLD 0x40000000
#define STARS_SMALL_SIGNED_ADD_CONSTANT_THRESHOLD ((int)(-STARS_LARGE_SIGNED_ADD_CONSTANT_THRESHOLD))
bool SMPInstr::IsAdditionOfLargeConstant(uval_t &ConstValue, unsigned short SignMask) {
bool LargeConstFound = false;
if (this->MDIsOverflowingOpcode() && (SignMask != FG_MASK_INCONSISTENT_SIGN) && (SignMask != 0)) {
op_t AddendOp = this->AddSubSourceOp;
if (o_void != AddendOp.type) {
set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(AddendOp);
if (this->FindConstantValue(UseIter, ConstValue)) {
if (FG_MASK_UNSIGNED == SignMask) {
if (((uval_t) STARS_LARGE_UNSIGNED_ADD_CONSTANT_THRESHOLD) <= ConstValue) {
LargeConstFound = true;
}
}
else if (FG_MASK_SIGNED == SignMask) {
int SignedConstValue = (int) ConstValue;
if ( (((int) STARS_LARGE_SIGNED_ADD_CONSTANT_THRESHOLD) <= SignedConstValue)
|| (((int) STARS_SMALL_SIGNED_ADD_CONSTANT_THRESHOLD) >= SignedConstValue)) {
LargeConstFound = true;
}
}
}
}
}
return LargeConstFound;
} // end of SMPInstr::IsAdditionOfLargeConstant()
// MACHINE DEPENDENT: Is instruction a return instruction?
bool SMPInstr::MDIsReturnInstr(void) const {
return ((this->SMPcmd.itype == NN_retn) || (this->SMPcmd.itype == NN_retf));
}
// MACHINE DEPENDENT: Is instruction a POP instruction?
#define FIRST_POP_INST NN_pop
#define LAST_POP_INST NN_popfq
bool SMPInstr::MDIsPopInstr(void) const {
return ((this->SMPcmd.itype >= FIRST_POP_INST)
&& (this->SMPcmd.itype <= LAST_POP_INST));
}
// MACHINE DEPENDENT: Is instruction a PUSH instruction?
#define FIRST_PUSH_INST NN_push
#define LAST_PUSH_INST NN_pushfq
bool SMPInstr::MDIsPushInstr(void) const {
return ((this->SMPcmd.itype >= FIRST_PUSH_INST)
&& (this->SMPcmd.itype <= LAST_PUSH_INST));
}
// MACHINE DEPENDENT: Is instruction an ENTER instruction?
bool SMPInstr::MDIsEnterInstr(void) const {
return ((this->SMPcmd.itype >= MD_FIRST_ENTER_INSTR)
&& (this->SMPcmd.itype <= MD_LAST_ENTER_INSTR));
}
// MACHINE DEPENDENT: Is instruction a LEAVE instruction?
bool SMPInstr::MDIsLeaveInstr(void) const {
return ((this->SMPcmd.itype >= MD_FIRST_LEAVE_INSTR)
&& (this->SMPcmd.itype <= MD_LAST_LEAVE_INSTR));
}
// MACHINE DEPENDENT: Is instruction a HALT instruction?
bool SMPInstr::MDIsHaltInstr(void) const {
return (NN_hlt == this->SMPcmd.itype);
}
#define MD_FIRST_COND_MOVE_INSTR NN_cmova
#define MD_LAST_COND_MOVE_INSTR NN_fcmovnu
// MACHINE DEPENDENT: Is instruction a conditional move?
bool SMPInstr::MDIsConditionalMoveInstr(void) const {
return ((this->SMPcmd.itype >= MD_FIRST_COND_MOVE_INSTR)
&& (this->SMPcmd.itype <= MD_LAST_COND_MOVE_INSTR));
}
// MACHINE DEPENDENT: Is instruction any kind of move?
bool SMPInstr::MDIsMoveInstr(void) const {
return ((NN_mov == this->SMPcmd.itype) || (NN_movsx == this->SMPcmd.itype)
|| (NN_movzx == this->SMPcmd.itype) || this->MDIsConditionalMoveInstr());
}
// MACHINE DEPENDENT: Do opcode/operands definitely indicate signed arithmetic?
// Generally, this is only true for certain variants of multiplication and division.
bool SMPInstr::MDIsSignedArithmetic(void) const {
unsigned short opcode = this->SMPcmd.itype;
if (NN_idiv == opcode)
return true;
if (NN_imul == opcode) {
// If we discard the upper N bits of the multiplication result, then the
// lower N bits are the same for signed and unsigned multiplication, and
// gcc/g++ often use the IMUL opcode for both signed and unsigned multiplies
// when only N bits of result are retained. Therefore, the SIGNED nature of
// IMUL operands can only be inferred from the case in which 2N bits are kept.
return (!(this->AreMultiplicationBitsDiscarded()));
}
else { // idiv and imul are only possible signed cases
return false;
}
} // end of SMPInstr::MDIsSignedArithmetic()
// MACHINE DEPENDENT: Is instruction a conditional jump based on an unsigned condition?
bool SMPInstr::MDIsUnsignedBranch(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_ja == opcode) || (NN_jae == opcode) || (NN_jb == opcode) || (NN_jbe == opcode)
|| (NN_jna == opcode) || (NN_jnae == opcode) || (NN_jnb == opcode) || (NN_jnbe == opcode));
}
// MACHINE DEPENDENT: Is instruction a conditional jump based on a signed condition?
bool SMPInstr::MDIsSignedBranch(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_jg == opcode) || (NN_jge == opcode) || (NN_jl == opcode) || (NN_jle == opcode)
|| (NN_jng == opcode) || (NN_jnge == opcode) || (NN_jnl == opcode) || (NN_jnle == opcode)
|| (NN_js == opcode) || (NN_jns == opcode));
}
// MACHINE DEPENDENT: Is instruction a boolean set based on an unsigned condition?
bool SMPInstr::MDIsUnsignedSetValue(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_seta == opcode) || (NN_setae == opcode) || (NN_setb == opcode) || (NN_setbe == opcode)
|| (NN_setna == opcode) || (NN_setnae == opcode) || (NN_setnb == opcode) || (NN_setnbe == opcode));
}
// MACHINE DEPENDENT: Is instruction a boolean set based on a signed condition?
bool SMPInstr::MDIsSignedSetValue(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_setg == opcode) || (NN_setge == opcode) || (NN_setl == opcode) || (NN_setle == opcode)
|| (NN_setng == opcode) || (NN_setnge == opcode) || (NN_setnl == opcode) || (NN_setnle == opcode)
|| (NN_sets == opcode) || (NN_setns == opcode));
}
// MACHINE DEPENDENT: Is instruction a boolean set based on any condition?
bool SMPInstr::MDIsAnySetValue(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_seta <= opcode) && (NN_setz >= opcode));
}
// Is kind of shift or rotate that is used in hash functions
#define STARS_HASH_SHIFT_THRESHOLD 4
bool SMPInstr::MDIsHashingArithmetic(void) const {
bool FoundHashShift = false;
unsigned short opcode = this->SMPcmd.itype;
// We are looking for shifts or rotates in the leftward direction.
if ((opcode == NN_rcl) || (opcode == NN_rol) || (opcode == NN_shl) || (opcode == NN_shld)) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
assert(CurrRT->HasRightSubTree());
CurrRT = CurrRT->GetRightTree();
op_t ShiftCountOp = CurrRT->GetRightOperand();
if (o_imm == ShiftCountOp.type) {
uval_t CountValue = ShiftCountOp.value;
FoundHashShift = (CountValue >= STARS_HASH_SHIFT_THRESHOLD);
}
}
return FoundHashShift;
} // end of SMPInstr::MDIsHashingArithmetic()
// Detect comparison of non-immediate to immediate. Return the two operands if true.
bool SMPInstr::MDIsCompareToPositiveConstant(op_t &NonConstOperand, uval_t &ConstValue) const {
bool CompareToConst = false;
if (NN_cmp == this->SMPcmd.itype) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
assert(CurrRT->HasRightSubTree());
CurrRT = CurrRT->GetRightTree();
assert(!(CurrRT->HasRightSubTree()));
op_t LeftOp = CurrRT->GetLeftOperand();
op_t RightOp = CurrRT->GetRightOperand();
if (o_imm == RightOp.type) {
CompareToConst = true;
ConstValue = RightOp.value;
NonConstOperand = LeftOp;
}
else if (o_imm == LeftOp.type) { // rare to see immediate first
CompareToConst = true;
ConstValue = LeftOp.value;
NonConstOperand = RightOp;
}
}
return CompareToConst;
} // end of SMPInstr::MDIsCompareToPositiveConstant()
bool SMPInstr::MDIsSubtractionOfConstant(op_t &NonConstOperand, uval_t &ConstValue) const {
bool SubtractOfConst = false;
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
if (CurrRT->HasRightSubTree()) {
CurrRT = CurrRT->GetRightTree();
SMPoperator CurrOp = CurrRT->GetOperator();
if (SMP_SUBTRACT == CurrOp) {
assert(!(CurrRT->HasRightSubTree()));
op_t LeftOp = CurrRT->GetLeftOperand();
op_t RightOp = CurrRT->GetRightOperand();
if (o_imm == RightOp.type) {
SubtractOfConst = true;
ConstValue = RightOp.value;
NonConstOperand = LeftOp;
}
}
else if (this->MDIsLoadEffectiveAddressInstr() && (SMP_ADD == CurrOp)) {
// We could have an addition of a negative constant via an lea opcode,
// e.g. lea ecx,[eax-48] will look like ecx := eax+(-48) in the RTL.
if (!(CurrRT->HasRightSubTree())) {
op_t LeftOp = CurrRT->GetLeftOperand();
op_t RightOp = CurrRT->GetRightOperand();
if (o_imm == RightOp.type) {
ConstValue = RightOp.value;
int SignedConstValue = (int) ConstValue;
if (0 > SignedConstValue) {
SubtractOfConst = true;
NonConstOperand = LeftOp;
ConstValue = (uval_t)(-SignedConstValue); // make +(-x) into -(+x)
}
}
}
}
}
return SubtractOfConst;
} // end of SMPInstr::MDIsSubtractionOfConstant()
// is AND operation that masks off lower BytesMasked bytes
bool SMPInstr::MDIsSubregMaskInst(size_t &BytesMasked) {
bool MaskingFound = false;
if (this->MDIsBitwiseAndOpcode()) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
if (CurrRT->HasRightSubTree()) {
CurrRT = CurrRT->GetRightTree();
SMPoperator CurrOp = CurrRT->GetOperator();
assert(SMP_BITWISE_AND == CurrOp);
op_t RightOp = CurrRT->GetRightOperand();
if (o_imm == RightOp.type) {
uval_t MaskValue = RightOp.value;
if (0x01000000 > MaskValue) {
MaskingFound = true;
if (0x100 > MaskValue) {
BytesMasked = 1;
}
else if (0x10000 > MaskValue) {
BytesMasked = 2;
}
else {
BytesMasked = 3;
}
}
}
}
}
return MaskingFound;
}
// MACHINE DEPENDENT: Does instruction use a callee-saved register?
bool SMPInstr::MDUsesCalleeSavedReg(void) {
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) {
op_t CurrOp = CurrUse->GetOp();
if (CurrOp.is_reg(R_bp) || CurrOp.is_reg(R_si)
|| CurrOp.is_reg(R_di) || CurrOp.is_reg(R_bx)) {
return true;
}
}
return false;
} // end of SMPInstr::MDUsesCalleeSavedReg()
// Is the instruction a register to register copy of a stack pointer or frame pointer
// into a general purpose register (which mmStrata will now need to track as a stack
// relative pointer)?
bool SMPInstr::MDIsStackPointerCopy(bool UseFP) {
// OptType 3 indicates a move instruction
// The lea instruction can perform three operand arithmetic, e.g.
// lea ebx,[esp+12] is just ebx:=esp+12, so it is a stack pointer copy.
if (((this->OptType == 3) || (NN_lea == this->SMPcmd.itype))
&& (this->GetFirstDef()->GetOp().type == o_reg)
&& (!(this->GetFirstDef()->GetOp().is_reg(R_sp)))
&& (!(this->HasSourceMemoryOperand()))) { // reg to reg move
if (UseFP) {
if (this->GetFirstUse()->GetOp().is_reg(R_bp))
// Move of base pointer EBP into a general register
return true;
else if ((this->GetFirstUse()->GetOp().is_reg(R_sp))
&& !(this->GetFirstDef()->GetOp().is_reg(R_bp)))
// Move of ESP into something besides a base pointer
return true;
}
else if (this->GetFirstUse()->GetOp().is_reg(R_sp)) {
// Move of ESP into a register; no base pointer used in this function
return true;
}
}
return false;
} // end of SMPInstr::MDIsStackPointerCopy()
// Does any RTL fit the alloca() pattern: stack_pointer -= non-immediate-operand
bool SMPInstr::HasAllocaRTL(void) {
bool FoundAlloca = false;
size_t RTLCount = this->RTL.GetCount();
size_t RTLIndex;
for (RTLIndex = 0; RTLIndex < RTLCount; ++RTLIndex) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(RTLIndex);
if (CurrRT->IsAllocaRTL()) {
FoundAlloca = true;
break;
}
}
return FoundAlloca;
} // end of SMPInstr::HasAllocaRTL()
// Determine if the instruction saves or restores a pointer into the stack frame.
// If it saves a stack pointer, set Save to true, set the StackDelta saved, and set
// the operand that received the saved stack pointer into CopyOp. and return true.
// If it restores a stack pointer, set Save to false, set CopyOp to the operand that
// held the value being restored, set RestoreOp to the stack pointer or frame pointer
// register (whichever was restored), leave StackDelta alone for later computation
// based on reaching definitions, and return true.
// For most instructions, no save or restore of a stack pointer, so return false.
bool SMPInstr::MDIsStackPtrSaveOrRestore(bool UseFP, sval_t FPDelta, bool &Save, sval_t &StackDelta, op_t &CopyOp, bool &Error) {
bool StackPointerSaveOrRestore;
size_t RTLCount = this->RTL.GetCount();
size_t RTLIndex;
op_t TempOp;
int BaseReg, IndexReg, CopyReg;
ushort Scale;
ea_t offset;
SMPoperator CurrOper;
bool LookUpStackDelta; // Get stack delta from reaching defs for TempOp
sval_t DeltaAdjust; // add to StackDelta after computing from reaching defs, e.g. lea esp,[ecx-4] get TempOp of ecx
// and DeltaAdjust of -4
Error = false;
for (RTLIndex = 0; RTLIndex < RTLCount; ++RTLIndex) {
bool FPRestore = false; // frame pointer is restored
bool SPRestore = false; // stack pointer is restored
StackPointerSaveOrRestore = false; // default unless we detect a save or restore of the stack or frame pointer
TempOp = InitOp;
LookUpStackDelta = false;
DeltaAdjust = 0;
Save = false; // default unless we detect a stack pointer save
// The stack alignment instructions (SP := SP bitwise_and immediate_value)
// look like something that needs to be processed here, but we always ignore
// these instructions. They have a variable effect on the stack pointer, from zero
// to -15 delta, but we assume that the delta is zero. This works for us because
// no stack accesses will occur into the padding region.
// Also, any instruction that definitely does not restore the stack pointer or
// frame pointer from an arbitrary register or memory location, e.g. a leave instruction
// in x86 CPUs, is already handled in normal stack delta computations and needs
// no lookups from reaching defs, etc.
if (this->IsStackAlignmentInst() || this->MDIsLeaveInstr() || this->MDIsFrameAllocInstr()) {
break; // exit and return false
}
SMPRegTransfer *CurrRT = this->RTL.GetRT(RTLIndex);
CurrOper = CurrRT->GetOperator();
if (SMP_ASSIGN != CurrOper) {
break; // not a regular RTL
}
op_t LeftOp = CurrRT->GetLeftOperand();
if (LeftOp.is_reg(MD_STACK_POINTER_REG)) {
SPRestore = true; // temporary; might just be a push or pop RTL, etc., in which case we will reset.
}
else if (UseFP && LeftOp.is_reg(MD_FRAME_POINTER_REG)) {
FPRestore = true; // likewise temporary
}
if (!(SPRestore || FPRestore)) {
#if 0
if (LeftOp.is_reg(MD_FLAGS_REG)) {
break; // No point in looking for a save into the flags register
}
#endif
Save = true; // Maybe; keep looking for save
}
// If we are assigning to the stack pointer reg or the frame pointer reg, we need to analyze the right
// hand side of the RTL to see if it is a stack/frame pointer value, and not a simple push, pop, etc.
if (!(CurrRT->HasRightSubTree())) {
// Simple assignment.
op_t RightOp = CurrRT->GetRightOperand();
if ((o_reg <= RightOp.type) && (o_displ >= RightOp.type)) { // register or memory
if (RightOp.is_reg(MD_STACK_POINTER_REG)) {
// Stack pointer reg is being saved.
Save = true;
StackDelta = this->GetStackPtrOffset(); // LeftOp := SP, so saved delta is just current delta
CopyOp = LeftOp;
StackPointerSaveOrRestore = true;
FPRestore = false; // treat FP := SP as a save of SP rather than a restoration of FP
break;
}
else if (!SPRestore && UseFP && RightOp.is_reg(MD_FRAME_POINTER_REG)) {
// Frame pointer is being saved
Save = true;
StackDelta = FPDelta;
CopyOp = LeftOp;
StackPointerSaveOrRestore = true;
break;
}
else if (SPRestore || FPRestore) {
// stack or frame pointer is being restored; leave Save=false and set other outgoing arguments.
TempOp = RightOp;
CopyOp = RightOp;
StackPointerSaveOrRestore = true;
LookUpStackDelta = true;
}
else { // RightOp is register or non-stack-pointer memory expr; either might hold stack delta
TempOp = RightOp;
CopyOp = LeftOp;
LookUpStackDelta = true; // See if RightOp is holding a stack delta
StackPointerSaveOrRestore = true; // Maybe; flag tells us to keep looking
}
}
else {
if (SPRestore || FPRestore) {
SMP_msg("ERROR: Invalid operand type for assignment to stack or frame pointer at %x\n", this->GetAddr());
}
StackPointerSaveOrRestore = false;
break;
}
}
else { // we have a right subtree in the CurrRT
SMPRegTransfer *RightRT = CurrRT->GetRightTree();
// In order to have a right subtree, we must have something like:
// lea esp,[ecx-4] which produces the RTL: esp := ecx - 4
// We should consider any other RTL structure besides a basic addition or
// subtraction on the right subtree to be invalid.
CurrOper = RightRT->GetOperator();
if ((SMP_ADD == CurrOper) || (SMP_SUBTRACT == CurrOper)) {
op_t RightLeftOp = RightRT->GetLeftOperand();
if (o_reg == RightLeftOp.type) {
if (RightRT->HasRightSubTree()) {
// Complex RTL such as lea esp,[ebx+ecx*4] ; cannot analyze
StackPointerSaveOrRestore = false;
}
else {
op_t RightRightOp = RightRT->GetRightOperand();
if (o_imm != RightRightOp.type) {
// Complex RTL such as lea esp,[ebx+ecx] ; cannot analyze
StackPointerSaveOrRestore = false;
}
else {
TempOp = RightLeftOp;
DeltaAdjust = (sval_t) RightRightOp.value;
if (SMP_SUBTRACT == CurrOper) {
// Negate the stack delta adjustment, e.g. lea esp,[ecx-4] needs DeltaAdjust of -4, not 4.
DeltaAdjust = (0 - DeltaAdjust);
}
LookUpStackDelta = true;
StackPointerSaveOrRestore = true;
if (SPRestore || FPRestore) {
CopyOp = RightLeftOp;
}
else {
CopyOp = LeftOp;
}
}
}
}
else { // weird RTL; LeftOp := (MemoryOp OPER ???)
StackPointerSaveOrRestore = false;
}
}
else { // not ADD or SUBTRACT
StackPointerSaveOrRestore = false;
}
}
if (LookUpStackDelta) {
bool StackAccess = false;
bool NonStackMemAccess = false;
// We need to set StackDelta based on the reaching defs for TempOp
// A reg is probably a general register, but could have lea ebx,[esp+4] so it could be stack or frame pointer.
if (TempOp.is_reg(MD_STACK_POINTER_REG)) {
// Weed out RTs that increment or decrement the stack pointer, e.g. SP := SP -4.
// These are not the kind of "save" or "restore" RTs that we are tracking.
if (CopyOp.is_reg(MD_STACK_POINTER_REG)) {
StackPointerSaveOrRestore = false;
SPRestore = false;
FPRestore = false;
Save = false;
}
else {
StackDelta = this->GetStackPtrOffset();
StackDelta += DeltaAdjust;
LookUpStackDelta = false; // just got it; no need for reaching defs
StackPointerSaveOrRestore = true;
}
}
else if (UseFP && TempOp.is_reg(MD_FRAME_POINTER_REG)) {
StackDelta = FPDelta;
StackDelta += DeltaAdjust;
LookUpStackDelta = false; // just got it; no need for reaching defs
StackPointerSaveOrRestore = true;
}
else if (o_reg == TempOp.type) { // general reg, not frame or stack pointer reg
CopyReg = TempOp.reg;
}
else {
MDExtractAddressFields(TempOp, BaseReg, IndexReg, Scale, offset);
CopyReg = BaseReg;
bool IndexedAccess = ((R_none != BaseReg) && (R_none != IndexReg));
if (IndexedAccess) {
StackPointerSaveOrRestore = false; // Cannot analyze indexed accesses into the stack
}
else if (MDIsStackPtrReg(BaseReg, UseFP)) {
StackAccess = true;
}
else {
// memory expr that is not stack or frame pointer
NonStackMemAccess = true; // something like [ecx] might actually turn out to be stack access
DeltaAdjust = (sval_t) TempOp.addr; // get normalized delta from addr field
}
}
if (StackPointerSaveOrRestore && LookUpStackDelta) {
op_t FindOp = InitOp;
if (StackAccess) {
FindOp = TempOp;
}
else {
FindOp.type = o_reg;
FindOp.reg = CopyReg;
}
if (this->GetBlock()->GetFunc()->IsInStackPtrCopySet(FindOp)) {
// Screened out time wasters that are not in copy set; now,
// look up reaching defs.
// We need to find out which are the reaching definitions for the FindOp at the current InstAddr.
this->GetBlock()->GetFunc()->ComputeTempReachingDefs(FindOp, this->GetAddr());
this->GetBlock()->GetFunc()->ComputeTempStackDeltaReachesList(FindOp);
// See if TempStackDeltaReachesList has a consistent delta value.
StackPointerSaveOrRestore = this->GetBlock()->GetFunc()->FindReachingStackDelta(StackDelta); // consistent SavedDelta value across entire list
StackDelta += DeltaAdjust;
if (StackPointerSaveOrRestore && NonStackMemAccess) {
// We have something like [ecx] or [ecx+DeltaAdjust]. It turns out that
// ECX has a copy of the stack pointer or frame pointer in it, but that
// does not mean that the memory location [ecx] has a copy of a stack or
// frame pointer. We need to look up the normalized stack address [esp+StackDelta]
// in the StackPtrCopySet just like we did for ECX before we conclude that a
// stack pointer save or restore is happening.
FindOp = InitOp;
FindOp.type = o_displ;
FindOp.reg = MD_STACK_POINTER_REG;
FindOp.addr = StackDelta;
if (this->GetBlock()->GetFunc()->IsInStackPtrCopySet(FindOp)) {
// Screened out time wasters that are not in copy set; now,
// look up reaching defs.
// We need to find out which are the reaching definitions for the FindOp at the current InstAddr.
this->GetBlock()->GetFunc()->ComputeTempReachingDefs(FindOp, this->GetAddr());
this->GetBlock()->GetFunc()->ComputeTempStackDeltaReachesList(FindOp);
// See if TempStackDeltaReachesList has a consistent delta value.
StackPointerSaveOrRestore = this->GetBlock()->GetFunc()->FindReachingStackDelta(StackDelta); // consistent SavedDelta value across entire list
// StackPointerSaveOrRestore will now be true only if [ecx] pointed to a saved stack or frame pointer with consistent delta.
}
else {
// E.g. [ecx] pointed to stack location that was not holding a saved stack or frame pointer.
StackPointerSaveOrRestore = false;
}
}
}
else {
StackPointerSaveOrRestore = false; // reset, not in stack pointer copy set
}
}
} // end if (LookupStackDelta)
if (!StackPointerSaveOrRestore && !Save && (SPRestore || FPRestore)) {
// Any restore that could not be analyzed is an error.
Error = true;
break; // error exit
}
else if (StackPointerSaveOrRestore) {
if (FPRestore) {
// If we succeeded in looking up a stack delta that goes into the frame pointer reg,
// then we want to consider this instruction to be a save of a stack delta into
// a register (which happens to be the frame pointer reg in this case).
FPRestore = false;
Save = true;
}
break; // assume only one save or restore in an instruction; exit with success
}
} // end for all RTs in the RTL
return StackPointerSaveOrRestore;
} // end of SMPInstr::MDIsStackPtrSaveOrRestore()
// If call instruction is to malloc(), set the DEF register EAX type to
// HEAPPTR and return true.
bool SMPInstr::MDFindMallocCall(op_t TargetOp) {
bool changed = false;
func_t *TargetFunc = get_func(TargetOp.addr);
if (TargetFunc) {
char FuncName[MAXSTR];
get_func_name(TargetFunc->startEA, FuncName, sizeof(FuncName) - 1);
if (0 == strcmp("malloc", FuncName)) {
// NOTE: Some compilers might call it __malloc ; make this more robust !!!
#if SMP_VERBOSE_FIND_POINTERS
SMP_msg("Found call to malloc at %x\n", this->addr);
#endif
op_t SearchOp = InitOp;
SearchOp.type = o_reg;
SearchOp.reg = R_ax;
set<DefOrUse, LessDefUse>::iterator EAXDEF;
EAXDEF = this->SetDefType(SearchOp, HEAPPTR);
int SSANum = EAXDEF->GetSSANum();
changed = true;
if (this->BasicBlock->IsLocalName(SearchOp)) {
(void) this->BasicBlock->PropagateLocalDefType(SearchOp, HEAPPTR,
this->GetAddr(), SSANum, false);
}
else { // global name
this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false
(void) this->BasicBlock->PropagateGlobalDefType(SearchOp, HEAPPTR,
SSANum, false);
}
} // end if "malloc"
} // end if (TargetFunc)
return changed;
} // end of SMPInstr::MDFindMallocCall()
// Is instruction a branch (conditional or unconditional) to a
// code target that is not in the current chunk?
bool SMPInstr::IsBranchToFarChunk(void) {
if (this->IsFarBranchComputed()) { // answer is cached
return this->IsBranchesToFarChunk();
}
func_t *CurrChunk = get_fchunk(this->address);
bool FarBranch = false;
if ((JUMP | COND_BRANCH) & this->GetDataFlowType()) {
// Instruction is a direct branch, conditional or unconditional
if (this->NumUses() > 0) {
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) {
op_t JumpTarget = CurrUse->GetOp();
if ((o_near == JumpTarget.type) || (o_far == JumpTarget.type)) {
// Branches to a code address
// stdclib sometimes has jumps to zero and calls to zero. These are dead code.
if (0 != JumpTarget.addr) {
func_t *TargetChunk = get_fchunk(JumpTarget.addr);
// Is target address within the same chunk as the branch?
FarBranch = (NULL == TargetChunk) || (CurrChunk->startEA != TargetChunk->startEA);
if (FarBranch) {
this->FarBranchTarget = JumpTarget.addr;
}
}
}
}
}
}
if (FarBranch) {
this->SetBranchesToFarChunk();
}
this->SetFarBranchComputed();
return FarBranch;
} // end of SMPInstr::IsBranchToFarChunk()
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetUseSSA(op_t CurrOp, int SSASub) {
return this->Uses.SetSSANum(CurrOp, SSASub);
};
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefSSA(op_t CurrOp, int SSASub) {
return this->Defs.SetSSANum(CurrOp, SSASub);
};
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetUseType(op_t CurrOp, SMPOperandType CurrType) {
return this->Uses.SetType(CurrOp, CurrType, this);
};
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefType(op_t CurrOp, SMPOperandType CurrType) {
return this->Defs.SetType(CurrOp, CurrType, this);
};
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefMetadata(op_t CurrOp, SMPMetadataType Status) {
return this->Defs.SetMetadata(CurrOp, Status);
};
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefIndWrite(op_t CurrOp, bool IndWriteFlag) {
return this->Defs.SetIndWrite(CurrOp, IndWriteFlag);
};
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetUseNoTruncate(op_t CurrOp, bool NoTruncFlag) {
return this->Uses.SetNoTruncation(CurrOp, NoTruncFlag);
};
set<DefOrUse, LessDefUse>::iterator SMPInstr::SetDefNoOverflow(op_t DefOp, bool NoOverflowFlag) {
return this->Defs.SetNoOverflow(DefOp, NoOverflowFlag);
};
// Set the DeadRegsBitmap entry for Regnum.
void SMPInstr::SetRegDead(size_t RegNum) {
this->DeadRegsBitmap.set(RegNum);
return;
}
// Analyze the instruction and its operands.
void SMPInstr::Analyze(void) {
bool DebugFlag = false;
if (0x8049b00 == this->address) {
// Setting up breakpoint line.
DebugFlag = true;
}
// Fill cmd structure with disassembly of instr
if (!SMPGetCmd(this->address, this->SMPcmd, this->features))
return;
unsigned short opcode = this->SMPcmd.itype;
// Record what type of instruction this is, simplified for the needs
// of data flow and type analysis.
this->type = DFACategory[opcode];
// Record optimization category.
this->OptType = OptCategory[opcode];
if ((NN_int == opcode) || (NN_into == opcode) || (NN_int3 == opcode)) {
this->SetInterrupt();
}
else {
this->ResetInterrupt();
}
// Fix the IDA Pro mistakes in the operand list.
this->MDFixupIDAProOperandList();
// See if instruction is an ASM idiom for clearing a register.
if ((NN_xor == opcode) || (NN_lea == opcode)) {
ushort FirstReg;
if (o_reg == this->SMPcmd.Operands[0].type) {
FirstReg = this->SMPcmd.Operands[0].reg;
op_t SecondOpnd = this->SMPcmd.Operands[1];
if (NN_xor == opcode) {
// Check for xor of reg with itself
if (SecondOpnd.is_reg(FirstReg)) {
this->SetRegClearIdiom();
}
}
else { // must be lea
// check for lea reg,[nobasereg+nonindexreg+0]
if ((SecondOpnd.type >= o_mem) && (SecondOpnd.type <= o_displ)) {
int BaseReg, IndexReg;
ushort ScaleFactor;
ea_t Offset;
MDExtractAddressFields(SecondOpnd, BaseReg, IndexReg, ScaleFactor, Offset);
if ((R_none == BaseReg) && (R_none == IndexReg) && (0 == Offset)) {
this->SetRegClearIdiom();
}
}
}
}
}
// See if instruction is simple nop or ASM idiom for nop.
if (this->MDIsNop()) {
this->SetNop();
}
// Build the DEF and USE lists for the instruction.
this->FindMemOps();
this->BuildSMPDefUseLists();
// Determine whether the instruction is a jump target by looking
// at its cross references and seeing if it has "TO" code xrefs.
SMP_xref_t xrefs;
for (bool ok = xrefs.SMP_first_to(this->address, XREF_FAR); ok; ok = xrefs.SMP_next_to()) {
if ((xrefs.GetFrom() != 0) && (xrefs.GetIscode())) {
this->SetJumpTarget();
break;
}
}
// If instruction is a call or indirect call, see if a call target has been recorded
// by IDA Pro.
if (this->GetDataFlowType() == INDIR_CALL) {
for (bool ok = xrefs.SMP_first_from(this->address, XREF_ALL);
ok;
ok = xrefs.SMP_next_from()) {
if ((xrefs.GetTo() != 0) && (xrefs.GetIscode())) {
// Found a code target, with its address in CurrXrefs.to
if (xrefs.GetTo() == (this->address + this->GetCmd().size)) {
// A call instruction will have two targets: the fall through to the
// next instruction, and the called function. We want to find
// the called function.
continue;
}
// We found a target, not the fall-through.
this->CallTarget = xrefs.GetTo();
SMP_msg("Found indirect call target %x at %x\n",
xrefs.GetTo(), this->address);
break;
}
} // end for all code xrefs
if (BADADDR == this->CallTarget) {
SMP_msg("WARNING: Did not find indirect call target at %x\n",
this->address);
}
} // end if INDIR_CALL
else if (this->GetDataFlowType() == CALL) {
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) {
optype_t OpType = CurrUse->GetOp().type;
if ((OpType == o_near) || (OpType == o_far)) {
this->CallTarget = CurrUse->GetOp().addr;
}
}
if (BADADDR == this->CallTarget) {
SMP_msg("ERROR: Target not found for direct call at %x\n", this->address);
}
}
if (DebugFlag) {
SMP_msg("Analyzed debug instruction at %x\n", this->address);
}
return;
} // end of SMPInstr::Analyze()
// Analyze the floating point NOP marker instruction at the top of the function.
void SMPInstr::AnalyzeMarker(void) {
// Fill member variable SMPcmd structure with disassembly of instr
(void) memset(&(this->SMPcmd), 0, sizeof(this->SMPcmd));
this->SMPcmd.itype = NN_fnop;
this->SMPcmd.size = 1;
this->SMPcmd.ea = this->address;
// Set the instr disassembly text.
DisAsmText.SetMarkerInstText(this->GetAddr());
// Record what type of instruction this is, simplified for the needs
// of data flow and type analysis.
this->type = DFACategory[this->SMPcmd.itype];
// Record optimization category.
this->OptType = OptCategory[this->SMPcmd.itype];
return;
} // end of SMPInstr::AnalyzeMarker()
// Detect oddities of call instructions, such as pseudo-calls that are
// actually jumps within a function
void SMPInstr::AnalyzeCallInst(ea_t FirstFuncAddr, ea_t LastFuncAddr) {
if (BADADDR != this->CallTarget) {
if (this->CallTarget == FirstFuncAddr) {
this->SetDirectRecursiveCall();
}
else {
this->ResetDirectRecursiveCall();
if ((this->CallTarget > FirstFuncAddr)
&& (this->CallTarget < LastFuncAddr)) {
this->SetCallUsedAsJump();
this->type = JUMP;
}
else {
this->ResetCallUsedAsJump();
}
}
}
return;
} // end of SMPInstr::AnalyzeCallInst()
sval_t SMPInstr::AnalyzeStackPointerDelta(sval_t IncomingDelta, sval_t PreAllocDelta) {
uint16 InstType = this->SMPcmd.itype;
sval_t InstDelta = StackAlteration[InstType];
SMPitype FlowType = this->GetDataFlowType();
bool TailCall = this->IsTailCall();
if (this->IsCallUsedAsJump() || this->MDIsInterruptCall() || this->IsCondTailCall()) {
// Call is used within function as a jump. Happens when setting up
// thunk offsets, for example; OR, call is an interrupt call, in which
// the interrupt return cleans up the stack, leaving a delta of zero, but
// we do not have the system call code to analyze, OR, the call is a conditional
// jump to another function (conditional tail call), in which case the current
// function must have a return statement to fall into which will clean up the
// only thing left on the stack (the return address) and the conditional jump
// has no effect on the stack pointer.
; // leave InstDelta equal to negative or zero value from StackAlterationTable[]
}
else if (this->IsRecursiveCall()) {
// We don't have the net stack delta for our own function yet, so we cannot
// look it up. We must assume that each call has no net effect on the stack delta.
// Alternatively, we could call this->GetBlock()->GetFunc()->GetStackDeltaForCallee() as below.
InstDelta = 0;
}
else if (this->IsAllocaCall()) {
InstDelta = STARS_DEFAULT_ALLOCA_SIZE;
}
else if ((CALL == FlowType) || (INDIR_CALL == FlowType) || TailCall) {
// A real call instruction, which pushes a return address on the stack,
// not a call used as a branch within the function. A return instruction
// will usually cancel out the stack push that is implicit in the call, which
// means that the function will have a net stack ptr delta of +4, which will
// cancel out the -4 value of the call instruction and set the delta to zero.
// However, this is not true in all cases, so we get the net stack ptr delta
// directly from the called function unless it is an unresolved indirect call,
// in which case we assume +4. !!!!****!!!! In the future, we could analyze
// the code around an unresolved indirect call to see if it seems to be
// removing items left on the stack by the callee.
// SPECIAL CASE: A jump used as a tail call will have a stack ptr effect that is equal
// to the net stack ptr effect of its target function, usually +4, whereas a jump
// would otherwise have a net stack ptr effect of 0.
ea_t CalledFuncAddr = this->GetCallTarget();
if ((BADADDR == CalledFuncAddr) || (0 == CalledFuncAddr)) {
InstDelta = 0;
}
else { // We have a call target
SMPFunction *CalleeFunc = this->GetBlock()->GetFunc()->GetProg()->FindFunction(CalledFuncAddr);
sval_t AdjustmentDelta;
if (CalleeFunc) {
if (!CalleeFunc->HasSTARSStackPtrAnalysisCompleted()) {
// Phase ordering issue in the call graph. A mutually recursive clique of functions has to
// be broken by starting processing somewhere, and all callees cannot be processed before
// we start. If we got our stack down to zero and then made a tail call, then we have to assume
// that the callee will use our return address, so we assume the default stack delta. If not a
// tail call, we ask our function to see if the information is available from IDA Pro analyses,
// or if it can be inferred from the fact that the call is followed by a stack adjustment.
if (TailCall) {
InstDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA;
SMP_msg("WARNING: Callee stack ptr analysis not yet performed at tail call inst %x ; normal delta assumed\n", this->GetAddr());
}
else {
AdjustmentDelta = this->GetBlock()->GetFunc()->GetStackDeltaForCallee(CalledFuncAddr);
InstDelta += AdjustmentDelta;
SMP_msg("WARNING: Callee stack ptr analysis not yet performed at inst %x ; stack adjustment used\n", this->GetAddr());
}
}
else if (!CalleeFunc->StackPtrAnalysisSucceeded()) {
// Callee analyses were done, but they failed. In order to proceed, we have to assume
// the same situation as we just did in the case where analyses have not been performed.
SMP_msg("WARNING: Callee stack ptr analysis failed at inst %x ; normal delta assumed\n", this->GetAddr());
if (TailCall) {
InstDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA;
}
else {
AdjustmentDelta = this->GetBlock()->GetFunc()->GetStackDeltaForCallee(this->GetAddr());
InstDelta += AdjustmentDelta;
}
}
else {
// Callee's analyses have succeeded, so get delta straight from callee.
InstDelta += CalleeFunc->GetNetStackPtrDelta();
}
}
else {
#if 0
SMP_msg("ERROR: SMPInstr::AnalyzeStackPointerDelta failed to find func at %x in inst %x\n",
CalledFuncAddr, this->GetAddr());
InstDelta = SMP_STACK_DELTA_ERROR_CODE;
#else
SMP_msg("ERROR: SMPInstr::AnalyzeStackPointerDelta failed to find func at %x in inst %x\n",
CalledFuncAddr, this->GetAddr());
if (TailCall) {
InstDelta = CALLING_CONVENTION_DEFAULT_FUNCTION_STACK_DELTA;
}
else {
InstDelta = 0;
}
#endif
}
}
} // end CALL or INDIR_CALL or TailCall case
else if (1 == InstDelta) {
// value of 1 is trigger to investigate the RTL for the
// true value, which cannot be found simply by table lookup
// In the special case of an x86 LEAVE instruction, the effect
// on the stack pointer is to deallocate the local frame size,
// plus pop the saved frame pointer into EBP. Helper functions
// need to know whether to look for this special case.
bool IsLeaveInstr = this->MDIsLeaveInstr();
InstDelta = this->RTL.TotalStackPointerAlteration(IsLeaveInstr, IncomingDelta, PreAllocDelta);
}
return InstDelta;
} // end of SMPInstr::AnalyzeStackPointerDelta()
// Total the stack adjustment bytes, as happens after a call to a function that leaves
// outgoing args on the stack or swallows incoming args from the stack.
sval_t SMPInstr::FindStackAdjustment(void) {
uint16 InstType = this->SMPcmd.itype;
sval_t InstDelta = StackAlteration[InstType];
if (1 == InstDelta) {
// value of 1 is trigger to investigate the RTL for the
// true value, which cannot be found simply by table lookup
// In the special case of an x86 LEAVE instruction, the effect
// on the stack pointer is to deallocate the local frame size,
// plus pop the saved frame pointer into EBP. Helper functions
// need to know whether to look for this special case.
bool IsLeaveInstr = this->MDIsLeaveInstr();
if (!IsLeaveInstr) {
InstDelta = this->RTL.TotalStackPointerAlteration(IsLeaveInstr, 0, 0);
}
else {
InstDelta = 0; // LEAVE is not the kind of instr we are looking for
}
}
return InstDelta;
} // end of SMPInstr::FindStackAdjustment()
// Normalize stack operands to have a displacement from the stack pointer value on entry to the function,
// rather than the current stack pointer value.
// UseFP indicates we are using a frame pointer in the function.
// FPDelta holds the stack delta (normalized) for the frame pointer.
// DefOp comes in with the operand to be normalized, and contains the normalized operand upon return.
// Return true if operand is a register or stack location, false otherwise (true => include in data flow analysis sets and SSA.)
bool SMPInstr::MDComputeNormalizedDataFlowOp(bool UseFP, sval_t FPDelta, op_t &DefOp) {
if (o_reg == DefOp.type) {
return true;
}
else if (MDIsStackAccessOpnd(DefOp, UseFP)) {
op_t OldOp = DefOp;
int SignedOffset = (int) DefOp.addr;
sval_t NormalizedDelta;
if (DefOp.hasSIB) {
// We must deal with a potentially indexed memory expression. We want to
// normalize two different cases here: e.g. [esp+ebx+4] will become [esp+ebx-24]
// and [ebp+ebx-8] will become [esp+ebx-12] after normalization. A wrinkle
// on the second case is when the base register and index register are swapped
// in the SIB byte, and we make [ebx+ebp-4] into [esp+ebx-12], which involves
// correcting the index/base reg order in the SIB, because an index reg of ESP
// is the SIB encoding for "no index register" and we cannot leave it like that.
int BaseReg = sib_base(DefOp);
int IndexReg = (int) sib_index(DefOp);
if (X86_STACK_POINTER_REG == IndexReg) // signifies no index register
IndexReg = R_none;
if (BaseReg == X86_STACK_POINTER_REG) {
// We probably have an indexed ESP-relative operand.
// We leave the sib byte alone and normalize the offset.
NormalizedDelta = this->GetStackPtrOffset() + (sval_t) SignedOffset;
}
else {
// Must be EBP-relative.
NormalizedDelta = FPDelta + (sval_t) SignedOffset;
// Unfortunately, when we are dealing with a SIB byte in the opcode, we cannot
// just say DefOp.reg = MD_STACK_POINTER_REG to convert from the frame pointer
// to the stack pointer. Instead, we have to get into the nasty machine code
// level and change the SIB bits that specify either the base register or the
// index register, whichever one is the frame pointer.
if (BaseReg == X86_FRAME_POINTER_REG) {
// The three least significant bits of the SIB byte are the base register.
// They must contain a 5, which is the x86 value for register EBP, and we
// want to convert it to a 4, denoting register ESP. We can just zero out
// the least significant bit to accomplish that.
DefOp.sib &= 0xfe;
}
else {
// We sometimes have an instruction in which the frame pointer is used as
// the "index" register in the SIB byte, and the true index register is
// in the "base" register position in the SIB byte.
assert(IndexReg == X86_FRAME_POINTER_REG);
// The true index reg is in the lowest three bits, while the next three
// bits must contain a 5 (register EBP) and we want to make them a 4 (ESP).
// We must swap base and index regs as we normalize (see explanation above).
char SIBtemp = DefOp.sib;
char SIBindex = SIBtemp & 0x38;
char SIBbase = SIBtemp & 0x07;
assert ((SIBindex >> 3) == 5); // must be EBP
SIBtemp &= 0xa0; // zero out lower 6 bits; upper 2 bits are scale factor - leave them alone
SIBtemp &= (SIBbase << 3); // make old base reg (e.g. ebx) into a proper index reg
SIBtemp |= 0x04; // make the new base reg be 4 (reg ESP)
DefOp.sib = SIBtemp;
}
this->SetFPNormalizedToSP();
// Add the stack pointer to the USE set for the instruction.
this->MDAddRegUse(X86_STACK_POINTER_REG, false);
}
}
else if (DefOp.reg == MD_FRAME_POINTER_REG) {
// If FPDelta is -4 and SignedOffset is +8, then we have [ebp+8] as DefOp, and this
// is equivalent to [esp+4] where esp has its entry value, i.e. this would be the first incoming
// argument. If SignedOffset is -12, we have [ebp-12] as DefOp, and this is [esp-16] when
// normalized to the entry point value of the stack pointer. In both cases, we can see that the
// normalized stack delta is just FPDelta+SignedOffset.
NormalizedDelta = FPDelta + (sval_t) SignedOffset;
// Now, we simply convert the memory operand from EBP to ESP and replace the SignedOffset with the
// NormalizedDelta just computed.
DefOp.reg = MD_STACK_POINTER_REG;
this->SetFPNormalizedToSP();
// Add the stack pointer to the USE set for the instruction.
this->MDAddRegUse(DefOp.reg, false);
}
else {
assert(DefOp.reg == MD_STACK_POINTER_REG);
// We only need to adjust the offset to reflect the change in the stack pointer since the function
// was entered, e.g. [esp+4] is normalized to [esp-28] if the current esp value is 32 less than it
// was upon function entry. We get the value "-32" in that case from a member variable.
NormalizedDelta = this->GetStackPtrOffset() + (sval_t) SignedOffset;
}
DefOp.addr = (ea_t) NormalizedDelta; // common to frame and stack pointer cases
if ((o_phrase == DefOp.type) && (0 != NormalizedDelta)) {
// mov [esp],eax has an [esp] operand of type o_phrase, because there is no
// displacement field. After normalization, it will have a displacement field, so
// it has become an operand like [esp-32] and is now type o_displ.
DefOp.type = o_displ;
}
this->GetBlock()->GetFunc()->AddNormalizedStackOperand(OldOp, this->GetAddr(), DefOp);
return true;
}
else {
return false;
}
} // end of SMPInstr::MDComputeNormalizedDataFlowOp()
// Normalize stack operands in all DEFs and USEs to have stack deltas relative to the function entry stack pointer.
// Return true if any stack DEFs or USEs were normalized.
bool SMPInstr::MDNormalizeStackOps(bool UseFP, sval_t FPDelta, bool Recomputing, sval_t DeltaIncrement) {
bool StackOpFound = false;
bool OpNormalized;
bool UniqueDEFMemOp = true; // Does DEFMemOp not match any DEFs?
bool UniqueUSEMemOp = true; // Does USEMemOp not match any USEs?
bool UniqueLeaUSEMemOp = true; // Does LeaUSEMemOp not match any USEs?
bool UniqueMoveSource = true; // Does MoveSource not match any USEs?
set<DefOrUse, LessDefUse>::iterator DefIter, UseIter;
list<pair<set<DefOrUse, LessDefUse>::iterator, op_t> > DefWorkList, UseWorkList;
list<pair<set<DefOrUse, LessDefUse>::iterator, op_t> >::iterator WorkIter;
op_t OldOp, NewOp;
// Find all the DEFs that need changing, and put their iterators into a list.
// Normalizing stack ops could change their sort order, hence we could skip over
// a DEF in the set by erasing a DEF and reinserting a normalized DEF, so we
// make all the changes after we iterate through the DEFS set.
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
OldOp = DefIter->GetOp();
NewOp = OldOp;
if ((o_reg != NewOp.type) && (o_imm != NewOp.type)) {
if (Recomputing) {
OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, true, NewOp);
}
else {
OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, NewOp);
}
if (OpNormalized) {
StackOpFound = true;
if (IsEqOp(OldOp, this->DEFMemOp)) {
UniqueDEFMemOp = false;
}
pair<set<DefOrUse, LessDefUse>::iterator, op_t> DefItem(DefIter, NewOp);
DefWorkList.push_back(DefItem);
}
}
}
// Now go through the DEF worklist and change stack operands to normalized stack operands.
for (WorkIter = DefWorkList.begin(); WorkIter != DefWorkList.end(); ++WorkIter) {
DefIter = WorkIter->first;
DefIter = this->Defs.SetOp(DefIter, WorkIter->second);
}
// Normalize op_t private data member DEFs.
if (Recomputing) {
OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueDEFMemOp, this->DEFMemOp);
}
else {
OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, this->DEFMemOp);
}
// Find all USEs that need changing, and build a second work list.
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
OldOp = UseIter->GetOp();
NewOp = OldOp;
if ((o_reg != NewOp.type) && (o_imm != NewOp.type)) {
if (Recomputing) {
OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, true, NewOp);
}
else {
OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, NewOp);
}
if (OpNormalized) {
StackOpFound = true;
if (IsEqOp(OldOp, this->USEMemOp)) {
UniqueUSEMemOp = false;
}
if (IsEqOp(OldOp, this->GetLeaMemUseOp())) {
UniqueLeaUSEMemOp = false;
}
if (IsEqOp(OldOp, this->MoveSource)) {
UniqueMoveSource = false;
}
pair<set<DefOrUse, LessDefUse>::iterator, op_t> UseItem(UseIter, NewOp);
UseWorkList.push_back(UseItem);
}
}
}
// Now go through the USE worklist and change stack operands to normalized stack operands.
for (WorkIter = UseWorkList.begin(); WorkIter != UseWorkList.end(); ++WorkIter) {
UseIter = WorkIter->first;
UseIter = this->Uses.SetOp(UseIter, WorkIter->second);
}
// Normalize op_t private data member USEs.
op_t TempLeaMemOp = this->GetLeaMemUseOp();
if (Recomputing) {
OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueUSEMemOp, this->USEMemOp);
OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueLeaUSEMemOp, TempLeaMemOp);
if (OpNormalized)
this->SetLeaMemUseOp(TempLeaMemOp);
OpNormalized = this->MDRecomputeNormalizedDataFlowOp(DeltaIncrement, UniqueMoveSource, this->MoveSource);
}
else {
OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, this->USEMemOp);
OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, TempLeaMemOp);
if (OpNormalized)
this->SetLeaMemUseOp(TempLeaMemOp);
OpNormalized = this->MDComputeNormalizedDataFlowOp(UseFP, FPDelta, this->MoveSource);
}
// Declare victory.
this->SetDefsNormalized();
return StackOpFound;
} // end of SMPInstr::MDNormalizeStackOps()
// Renormalize SP-relative stack operands in functions that call alloca() by adding DeltaIncrement to their stack displacements.
// DefOp comes in with the operand to be renormalized, and contains the normalized operand upon return.
// Return true if operand is a register or stack location, false otherwise (true => include in data flow analysis sets and SSA.)
bool SMPInstr::MDRecomputeNormalizedDataFlowOp(sval_t DeltaIncrement, bool UpdateMaps, op_t &DefOp) {
op_t OldOp = DefOp;
if (o_reg == DefOp.type) {
return true;
}
else if (MDIsStackAccessOpnd(DefOp, this->GetBlock()->GetFunc()->UsesFramePointer())) {
if (this->HasFPNormalizedToSP()) {
// FP-relative operands do no change in alloca() functions when the alloca()
// causes the SP to change.
return true;
}
// The remaining cases are simple. The ESP-relative displacement is incremented by
// DeltaIncrement, regardless of the presence of a SIB byte.
int SignedOffset = (int) DefOp.addr;
sval_t NormalizedDelta = DeltaIncrement + (sval_t) SignedOffset;
DefOp.addr = (ea_t) NormalizedDelta;
if ((o_phrase == DefOp.type) && (0 != NormalizedDelta)) {
// mov [esp],eax has an [esp] operand of type o_phrase, because there is no
// displacement field. After normalization, it will have a displacement field, so
// it has become an operand like [esp-32] and is now type o_displ.
DefOp.type = o_displ;
}
if (UpdateMaps) { // We don't update maps for duplicate entries, e.g. USEMemOp, DEFMemOp, MoveSource
this->GetBlock()->GetFunc()->AddNormalizedStackOperand(OldOp, this->GetAddr(), DefOp);
}
return true;
}
else {
return false;
}
} // end of SMPInstr::MDRecomputeNormalizedDataFlowOp()
// If NormOp is a normalized stack memory operand, unnormalize it.
void SMPInstr::MDGetUnnormalizedOp(op_t &NormOp) {
sval_t SignedOffset;
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if (this->AreDefsNormalized() && MDIsStackAccessOpnd(NormOp, UseFP)) {
if (this->HasFPNormalizedToSP()) {
// Need to convert NormOp back to frame-pointer-relative address.
if (NormOp.hasSIB) {
// Convert base register from stack pointer back to frame pointer.
NormOp.sib |= 0x01;
}
else {
NormOp.reg = MD_FRAME_POINTER_REG;
}
SignedOffset = (sval_t) NormOp.addr;
SignedOffset -= this->GetBlock()->GetFunc()->GetFramePtrStackDelta();
}
else {
// NormOp should remain stack-pointer-relative address, but it
// should be a positive offset from the current stack pointer instead
// of a negative offset from the entry point of the function.
SignedOffset = (sval_t) NormOp.addr;
SignedOffset -= this->GetStackPtrOffset();
assert(0 <= SignedOffset);
}
NormOp.addr = (ea_t) SignedOffset;
}
return;
} // end of SMPInstr::MDGetUnnormalizedOp()
// Find USE-not-DEF operand that is not the flags register.
op_t SMPInstr::GetSourceOnlyOperand(void) {
size_t OpNum;
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
if (this->features & DefMacros[OpNum]) { // DEF
;
}
else if (this->features & UseMacros[OpNum]) { // USE
op_t CurrOp = this->SMPcmd.Operands[OpNum];
if (!(CurrOp.is_reg(X86_FLAGS_REG))) {
return CurrOp;
}
}
}
// It is expected that increment, decrement, and floating point stores
// will not have a USE-only operand. Increment and decrement have an
// operand that is both USEd and DEFed, while the floating point stack
// registers are implicit in most floating point opcodes. Also, exchange
// and exchange-and-add instructions have multiple DEF-and-USE operands.
int TypeGroup = SMPTypeCategory[this->SMPcmd.itype];
if ((TypeGroup != 2) && (TypeGroup != 4) && (TypeGroup != 9) && (TypeGroup != 12)
&& (TypeGroup != 13)) {
SMP_msg("ERROR: Could not find source only operand at %x in %s\n",
this->address, DisAsmText.GetDisAsm(this->GetAddr()));
}
return InitOp;
} // end of SMPInstr::GetSourceOnlyOperand()
// Should apparent memory operands be ignored? e.g. lea opcode on x86
bool SMPInstr::MDIgnoreMemOps(void) {
bool leaInst = (NN_lea == this->SMPcmd.itype);
return leaInst;
}
// Find memory DEFs and USEs, store in DEFMemOp and USEMemOp
void SMPInstr::FindMemOps(void) {
size_t OpNum;
if (!(this->MDIgnoreMemOps())) {
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if ((TempOp.type >= o_mem) && (TempOp.type <= o_displ)) { // memory
if (this->features & DefMacros[OpNum]) { // DEF
if (this->DEFMemOp.type == o_void) { // only save first mem DEF
this->DEFMemOp = TempOp;
}
}
if (this->features & UseMacros[OpNum]) { // USE
if (this->USEMemOp.type == o_void) { // only save first mem USE
this->USEMemOp = TempOp;
}
}
}
} // end for (OpNum = 0; ...)
}
this->SetMemOpsFound();
return;
} // end of SMPInstr::FindMemOps()
// Fix problems with the operands list in SMPcmd.
void SMPInstr::MDFixupIDAProOperandList(void) {
// IDA Pro often takes the instruction imul eax,0x80 and creates the following operands and features bits:
// Opnd[0] = EAX, both DEF and USE
// Opnd[1] = EAX, just USE
// Opnd[2] = immediate, neither DEF nor USE
// Our RTL building keys in on the DEF/USE bits in features, so this looks like imul eax,eax to us.
// We want it to look like:
// Opnd[0] = EAX, both DEF and USE
// Opnd[1] = immediate, just USE
if (NN_imul == this->SMPcmd.itype) {
op_t Opnd2 = this->SMPcmd.Operands[2];
if ((!(this->features & DefMacros[2]))
&& (!(this->features & UseMacros[2]))) {
if (o_void != Opnd2.type) {
// We have a third operand that is neither DEF nor USE.
SMP_msg("INFO: Fixing IMUL operand list at %x\n", this->GetAddr());
this->Dump();
// Two cases: Operands[0] == Operands[1], e.g. imul eax,Opnd2
// or else three-operand form: e.g. imul eax,ecx,Opnd2
// For the three-operand form, make sure Opnd0 is DEF only, others
// are USE only. For the two-operand form, make sure Opnd0 is DEF and USE,
// Opnd1 is current Opnd2 and is USE only.
op_t Opnd0 = this->SMPcmd.Operands[0];
op_t Opnd1 = this->SMPcmd.Operands[1];
if (IsEqOp(Opnd0, Opnd1)) {
// No need for three-operand form.
this->features |= DefMacros[0];
this->features |= UseMacros[0];
this->SMPcmd.Operands[1] = Opnd2;
this->SMPcmd.Operands[2] = InitOp;
}
else { // Must have three-operand form.
this->features |= UseMacros[2]; // set missing USE bit.
this->features &= (~UseMacros[0]); // Ensure no USE of Opnd0.
}
this->Dump();
}
}
}
return;
} // SMPInstr::MDFixupIDAProOperandList()
// Fill the Defs and Uses private data members.
void SMPInstr::BuildSMPDefUseLists(void) {
size_t OpNum;
bool DebugFlag = (0x8049b00 == this->GetAddr());
bool WidthDoubler = this->MDDoublesWidth();
this->Defs.clear();
this->Uses.clear();
// Start with the Defs.
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
if (this->features & DefMacros[OpNum]) { // DEF
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (WidthDoubler) {
// Opcodes that sign-extend a byte to a word, or a word to a dword,
// have only one operand. It is implicit, and it is the shorter USE.
// That means the DEF will have the same width as the USE, e.g. if
// we are sign-extending AX to EAX, the USE and DEF both be AX without
// a special fix. We fix this problem with the DEF operand now.
if (TempOp.dtyp == dt_byte) {
TempOp.dtyp = dt_word;
TempOp.reg = MDCanonicalizeSubReg(TempOp.reg);
}
else if (TempOp.dtyp == dt_word) {
TempOp.dtyp = dt_dword;
TempOp.reg = MDCanonicalizeSubReg(TempOp.reg);
}
else if (TempOp.dtyp == dt_dword) {
TempOp.dtyp = dt_qword;
}
else {
SMP_msg("ERROR: Instruction operand %zu not 1,2, or 4 bytes at %x dtyp: %d\n",
OpNum, this->address, TempOp.dtyp);
}
}
if (MDKnownOperandType(TempOp)) {
if (DebugFlag) {
SMP_msg("DEBUG: Setting DEF for: ");
PrintOperand(TempOp);
SMP_msg("\n");
}
this->Defs.SetRef(TempOp);
}
}
} // end for (OpNum = 0; ...)
if (this->IsRegClearIdiom()) {
// Something like xor eax,eax clears eax but does not really
// use eax. It is the same as mov eax,0 and we don't want to
// extend the prior def-use chain for eax to this instruction
// by treating the instruction as xor eax,eax. Instead, we
// build the DEF and USE lists and RTL as if it were mov eax,0.
op_t ImmOp = InitOp;
ImmOp.type = o_imm;
ImmOp.value = 0;
this->Uses.SetRef(ImmOp, NUMERIC);
return;
}
// Now, do the Uses. Uses have special case operations, because
// any memory operand could have register uses in the addressing
// expression, and we must create Uses for those registers. For
// example: mov eax,[ebx + esi*2 + 044Ch]
// This is a two-operand instruction with one def: eax. But
// there are three uses: [ebx + esi*2 + 044Ch], ebx, and esi.
// The first use is an op_t of type o_phrase (memory phrase),
// which can be copied from cmd.Operands[1]. Likewise, we just
// copy cmd.Operands[0] into the defs list. However, we must create
// op_t types for register ebx and register esi and append them
// to the Uses list. This is handled by the machine dependent
// method MDFixupDefUseLists().
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
if (this->features & UseMacros[OpNum]) { // USE
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (MDKnownOperandType(TempOp)) {
if (DebugFlag) {
SMP_msg("DEBUG: Setting USE for: ");
PrintOperand(TempOp);
SMP_msg("\n");
}
this->Uses.SetRef(TempOp);
}
}
} // end for (OpNum = 0; ...)
return;
} // end of SMPInstr::BuildSMPDefUseLists()
// Declare a branch/jump to be a tail call, clean up def/use lists.
void SMPInstr::SetTailCall(void) {
this->booleans1 |= INSTR_SET_TAIL_CALL;
if (this->type == COND_BRANCH) {
this->SetCondTailCall();
}
else {
this->ResetCondTailCall();
}
this->CallTarget = this->FarBranchTarget;
this->type = RETURN;
this->GetBlock()->SetReturns(true);
// We want to add the caller-saved registers to the USEs and DEFs lists
this->MDAddRegDef(R_ax, false);
this->MDAddRegDef(R_cx, false);
this->MDAddRegDef(R_dx, false);
this->MDAddRegUse(R_ax, false);
this->MDAddRegUse(R_cx, false);
this->MDAddRegUse(R_dx, false);
} // end of SMPInstr::SetTailCall()
// record original Lea instruction [pseudo-]memory operand.
void SMPInstr::SetLeaMemUseOp(op_t NewLeaOperand) {
if (NULL != this->BasicBlock) {
this->GetBlock()->GetFunc()->AddLeaOperand(this->GetAddr(), NewLeaOperand);
}
return;
}
// If DefReg is not already in the DEF list, add a DEF for it.
void SMPInstr::MDAddRegDef(ushort DefReg, bool Shown, SMPOperandType Type) {
op_t TempDef = InitOp;
TempDef.type = o_reg;
TempDef.reg = DefReg;
if (Shown)
TempDef.set_showed();
else
TempDef.clr_showed();
this->Defs.SetRef(TempDef, Type);
return;
} // end of SMPInstr::MDAddRegDef()
// If UseReg is not already in the USE list, add a USE for it.
void SMPInstr::MDAddRegUse(ushort UseReg, bool Shown, SMPOperandType Type) {
op_t TempUse = InitOp;
TempUse.type = o_reg;
TempUse.reg = UseReg;
if (Shown)
TempUse.set_showed();
else
TempUse.clr_showed();
this->Uses.SetRef(TempUse, Type);
return;
} // end of SMPInstr::MDAddRegUse()
// Perform machine dependent ad hoc fixes to the def and use lists.
// For example, some multiply and divide instructions in x86 implicitly
// use and/or define register EDX. For memory phrase examples, see comment
// in BuildSMPDefUseLists().
void SMPInstr::MDFixupDefUseLists(void) {
// First, handle the uses hidden in memory addressing modes. Note that we do not
// care whether we are dealing with a memory destination operand or source
// operand, because register USEs, not DEFs, happen within the addressing expressions.
size_t OpNum;
SMPOperandType RefType;
unsigned short opcode = this->SMPcmd.itype;
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t displacement;
bool UseFP = true;
bool HasIndexReg = false;
bool SingleAddressReg = false;
bool leaInst = (NN_lea == opcode);
bool DebugFlag = (this->GetAddr() == 0x8086177);
if (DebugFlag) {
SMP_msg("DEBUG: Fixing up DEF-USE lists for debug location\n");
this->Dump();
}
#if SMP_BASEREG_POINTER_TYPE
// Some instructions are analyzed outside of any function or block when fixing up
// the IDB, so we have to assume the block and func pointers might be NULL.
if ((NULL != this->BasicBlock) && (NULL != this->BasicBlock->GetFunc()))
UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
#endif
if (DebugFlag) {
SMP_msg("DEBUG: UseFP = %d\n", UseFP);
}
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
op_t Opnd = SMPcmd.Operands[OpNum];
if ((Opnd.type == o_phrase) || (Opnd.type == o_displ) || (Opnd.type == o_mem)) {
MDExtractAddressFields(Opnd, BaseReg, IndexReg, ScaleFactor, displacement);
SingleAddressReg = ((0 == displacement)
&& ((R_none == BaseReg) || (R_none == IndexReg)));
if (R_none != IndexReg) {
op_t IndexOpnd = Opnd; // Init to current operand field values
IndexOpnd.type = o_reg; // Change type and reg fields
IndexOpnd.reg = (ushort) IndexReg;
IndexOpnd.hasSIB = 0;
IndexOpnd.set_showed();
if (0 == ScaleFactor)
this->Uses.SetRef(IndexOpnd);
else { // scaling == shift ==> NUMERIC
HasIndexReg = true;
this->Uses.SetRef(IndexOpnd, NUMERIC);
}
}
if (R_none != BaseReg) {
op_t BaseOpnd = Opnd; // Init to current operand field values
BaseOpnd.type = o_reg; // Change type and reg fields
BaseOpnd.reg = (ushort) BaseReg;
BaseOpnd.hasSIB = 0;
BaseOpnd.set_showed();
RefType = UNINIT;
#if SMP_BASEREG_POINTER_TYPE
// R_sp and R_bp will get type STACKPTR in SMPInstr::SetImmedTypes().
// Other registers used as base registers should get their USEs as
// base registers typed as POINTER, which might get refined later
// to STACKPTR, GLOBALPTR, HEAPPTR, etc.
// NOTE: the NN_lea opcode is often used without a true base register.
// E.g. lea eax,[eax+eax+5] is an x86 idiom for eax:=eax*2+5, which
// could not be done in one instruction without using the addressing
// modes of the machine to do the arithmetic. We don't want to set the
// USE of EAX to POINTER in this case, so we will conservatively skip
// all lea instructions here.
// We cannot be sure that a register is truly a base register unless
// there is also an index register. E.g. with reg+displacement, we
// could have memaddr+indexreg or basereg+offset, depending on what
// the displacement is. The exception is if there is no offset and only
// one addressing register, e.g. mov eax,[ebx].
if (BaseOpnd.is_reg(MD_STACK_POINTER_REG) || (UseFP && BaseOpnd.is_reg(MD_FRAME_POINTER_REG))
|| leaInst || (!HasIndexReg && !SingleAddressReg)) {
;
}
else {
RefType = POINTER;
}
#endif
this->Uses.SetRef(BaseOpnd, RefType);
} // end if R_none != BaseReg
} // end if (o_phrase or o_displ operand)
} // end for (all operands)
// The lea (load effective address) instruction looks as if it has
// a memory USE: lea ebx,[edx+esi]
// However, this instruction is really just: ebx := edx+esi
// Now that the above code has inserted the "addressing" registers
// into the USE list, we should remove the "memory USE".
if (leaInst) {
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) {
op_t UseOp = CurrUse->GetOp();
if ((o_mem <= UseOp.type) && (o_displ >= UseOp.type)) {
this->SetLeaMemUseOp(UseOp);
this->EraseUse(CurrUse);
this->USEMemOp = InitOp;
break;
}
}
}
// Next, handle repeat prefices in the instructions. The Intel REPE/REPZ prefix
// is just the text printed for SCAS/CMPS instructions that have a REP prefix.
// Only two distinct prefix codes are actually defined: REP and REPNE/REPNZ, and
// REPNE/REPNZ only applies to SCAS and CMPS instructions.
bool HasRepPrefix = (0 != (this->SMPcmd.auxpref & aux_rep));
bool HasRepnePrefix = (0 != (this->SMPcmd.auxpref & aux_repne));
if (HasRepPrefix && HasRepnePrefix)
SMP_msg("REP and REPNE both present at %x %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
if (HasRepPrefix || HasRepnePrefix) {
// All repeating instructions use ECX as the countdown register.
op_t BaseOpnd = InitOp;
BaseOpnd.type = o_reg; // Change type and reg fields
BaseOpnd.reg = R_cx;
BaseOpnd.clr_showed();
this->Defs.SetRef(BaseOpnd, NUMERIC);
this->Uses.SetRef(BaseOpnd, NUMERIC);
}
if ((opcode == NN_cmps) || (opcode == NN_scas) || (opcode == NN_movs) || (opcode == NN_stos)) {
// ESI and EDI are USEd and DEFed to point to source and dest strings for CMPS/MOVS.
// Only EDI is involved with SCAS/STOS.
op_t BaseOpnd = InitOp;
BaseOpnd.type = o_reg; // Change type and reg fields
BaseOpnd.clr_showed();
if ((opcode == NN_cmps) || (opcode == NN_movs)) {
BaseOpnd.reg = R_si;
this->Defs.SetRef(BaseOpnd, POINTER);
this->Uses.SetRef(BaseOpnd, POINTER);
}
BaseOpnd.reg = R_di;
this->Defs.SetRef(BaseOpnd, POINTER);
this->Uses.SetRef(BaseOpnd, POINTER);
}
else if ((NN_loopw <= opcode) && (NN_loopqne >= opcode)) {
op_t LoopCounterOp = InitOp;
LoopCounterOp.type = o_reg;
LoopCounterOp.reg = R_cx;
this->Defs.SetRef(LoopCounterOp, NUMERIC);
this->Uses.SetRef(LoopCounterOp, NUMERIC);
}
// Now, handle special instruction categories that have implicit operands.
if (NN_cmpxchg == opcode) {
// x86 Compare and Exchange conditionally sets EAX. We must keep data flow analysis
// sound by declaring that EAX is always a DEF.
this->MDAddRegDef(R_ax, false);
} // end if NN_cmpxchg
else if (this->MDIsPopInstr() || this->MDIsPushInstr() || this->MDIsReturnInstr()) {
// IDA does not include the stack pointer in the DEFs or USEs.
this->MDAddRegDef(R_sp, false);
this->MDAddRegUse(R_sp, false);
if (!this->MDIsReturnInstr()) {
// We always reference [esp+0] or [esp-4], so add it to the DEF or USE list.
op_t StackOp = InitOp;
StackOp.type = o_displ;
StackOp.reg = R_sp;
if (this->MDIsPopInstr()) {
StackOp.addr = 0; // [ESP+0]
this->Uses.SetRef(StackOp); // USE
}
else {
StackOp.addr = (ea_t) -4; // [ESP-4]
this->Defs.SetRef(StackOp); // DEF
}
}
}
#if SMP_CALL_TRASHES_REGS
else if ((this->type == CALL) || (this->type == INDIR_CALL) || this->IsTailCall()) {
// We want to add the caller-saved registers to the USEs and DEFs lists
this->MDAddRegDef(R_ax, false);
this->MDAddRegDef(R_cx, false);
this->MDAddRegDef(R_dx, false);
this->MDAddRegUse(R_ax, false);
this->MDAddRegUse(R_cx, false);
this->MDAddRegUse(R_dx, false);
#if 1
if (this->MDIsInterruptCall()) {
#endif
this->MDAddRegDef(R_bx, false);
this->MDAddRegUse(R_bx, false);
this->MDAddRegDef(R_si, false);
this->MDAddRegUse(R_si, false);
#if 1
}
#endif
}
#endif
else if (this->MDIsEnterInstr() || this->MDIsLeaveInstr()) {
// Entire function prologue or epilogue microcoded.
this->MDAddRegDef(R_sp, false);
this->MDAddRegUse(R_sp, false);
this->MDAddRegDef(R_bp, false);
this->MDAddRegUse(R_bp, false);
}
else if ((opcode == NN_maskmovq) || (opcode == NN_maskmovdqu)) {
this->MDAddRegUse(R_di, false, POINTER);
}
else if (8 == this->GetOptType()) {
// This category implicitly writes to EDX:EAX.
this->MDAddRegDef(R_dx, false);
this->MDAddRegDef(R_ax, false);
} // end else if (8 == GetOptType)
else if (7 == this->GetOptType()) {
// Category 7 instructions sometimes write implicitly to EDX:EAX or DX:AX.
// DX is the same as EDX to IDA Pro (and SMP); ditto for EAX and AX.
// DIV, IDIV, and MUL all have hidden EAX or AX operands (hidden in the IDA Pro
// sense, because they are not displayed in the disassembly text). For example:
// mul ebx means EDX:EAX <-- EAX*EBX, and mul bx means DX:AX <-- AX*BX. If the
// source operand is only 8 bits wide, there is room to hold the result in AX
// without using DX: mul bl means AX <-- AL*BL.
// IMUL has forms with a hidden EAX or AX operand and forms with no implicit
// operands: imul ebx means EDX:EAX <-- EAX*EBX, but imul ebx,edx means that
// EBX*EDX gets truncated and the result placed in EBX (no hidden operands).
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
op_t TempUse = this->SMPcmd.Operands[OpNum];
if (!TempUse.showed()) { // hidden operand
if (TempUse.is_reg(R_ax)) { // not R_al, so it is not 8 bits
if ((NN_div == this->SMPcmd.itype) || (NN_idiv == this->SMPcmd.itype)) {
this->MDAddRegUse(R_dx, false);
}
this->MDAddRegDef(R_ax, false);
this->MDAddRegDef(R_dx, false);
}
}
}
} // end else if (7 == OptType)
#if 0
// The floating point instructions in type categories 14 and 15 often USE and DEF
// the floating point register stack, e.g. pushing a value onto that stack is a
// massive copy downward of stack locations. We don't really care about the USE of
// the stack if the value being pushed came from elsewhere than the stack. For example,
// an "fld" opcode pushes its source onto the stack. We build RTLs with a simple
// move structure, but the RTL building can be fooled by seeing two "source" operands
// in the USE list.
if ((14 == SMPTypeCategory[this->SMPcmd.itype])
|| (15 == SMPTypeCategory[this->SMPcmd.itype])) {
}
#endif
#if 0 // Not true for LOOP instructions that use only the ECX counter register.
if (this->type == COND_BRANCH) {
assert(SMPUsesFlags[opcode]);
}
#endif
// The return value register EAX is not quite like a caller-save or callee-save
// register (technically, it is caller-save). Within a callee, it might appear
// that EAX has become dead by the time a return instruction is reached, but
// the USE that would make it not dead is in the caller. To prevent type inference
// from mistakenly thinking that all USEs of EAX have been seen in the callee,
// we add EAX to the USE list for all return instructions, as well as for all
// tail calls, which are essentially returns in terms of data flow analysis.
// This USE of EAX will always be of type UNINIT unless its DEF has a known type
// that propagates to it. Thus, it will prevent an invalid back inference of the
// DEF type from "all" USE types that are visible in the callee; even if they
// were all NUMERIC, this return USE will be UNINIT and inhibit the invalid
// type inference. EAX could be loaded with a pointer from memory, for example,
// and USEd only in a comparison instruction, making it falsely appear to be
// a NUMERIC, without this extra USE at the return instruction.
// Because some of the library functions pass values around in EBX, EDI, etc.,
// we will add these general purpose registers to the USE list for returns
// in order to prevent erroneous analyses of dead registers or unused
// metadata.
if ((this->type == RETURN) || this->IsTailCall()) {
this->MDAddRegUse(R_ax, false);
this->MDAddRegUse(R_bx, false);
this->MDAddRegUse(R_cx, false);
this->MDAddRegUse(R_dx, false);
if (!UseFP)
this->MDAddRegUse(R_bp, false);
this->MDAddRegUse(R_si, false);
this->MDAddRegUse(R_di, false);
}
// Next, add the flags register to the DEFs and USEs for those instructions that
// are marked as defining or using flags.
if (!this->IsDefsFlags() && SMPDefsFlags[opcode]) {
this->MDAddRegDef(X86_FLAGS_REG, false);
this->SetDefsFlags();
}
if (!this->IsUsesFlags() && SMPUsesFlags[opcode]) {
this->MDAddRegUse(X86_FLAGS_REG, false);
this->SetUsesFlags();
}
#if 1
if (this->IsNop()) {
// Clear the DEFs and USEs for no-ops.
// These include machine idioms for no-ops, e.g. mov esi,esi
// or xchg ax,ax or lea esi,[esi].
this->Defs.clear();
this->Uses.clear();
this->MoveSource = InitOp;
this->DEFMemOp = InitOp;
this->USEMemOp = InitOp;
this->SetLeaMemUseOp(InitOp);
this->OptType = 1;
}
#endif
if (DebugFlag) {
SMP_msg("DEBUG after MDFixupDefUseLists:\n");
this->Dump();
}
return;
} // end of SMPInstr::MDFixupDefUseLists()
// If we can definitely identify which part of the addressing expression
// used in MemOp is the POINTER type, and it is not a STACKPTR or GLOBALPTR
// immediate, set the USE type for that register to POINTER and return true.
// If we can find definite NUMERIC addressing registers that are not already
// typed as NUMERIC, set their USE types to NUMERIC and return true.
bool SMPInstr::MDFindPointerUse(op_t MemOp, bool UseFP) {
bool changed = false;
int BaseReg;
int IndexReg;
op_t BaseOp = InitOp;
op_t IndexOp = InitOp;
SMPOperandType BaseType = UNKNOWN;
SMPOperandType IndexType = UNKNOWN;
ushort ScaleFactor;
ea_t offset;
set<DefOrUse, LessDefUse>::iterator BaseIter;
set<DefOrUse, LessDefUse>::iterator IndexIter;
if (NN_lea == this->SMPcmd.itype)
return false; // lea instruction really has no memory operands
if (NN_fnop == this->SMPcmd.itype)
return false; // SSA marker instruction
MDExtractAddressFields(MemOp, BaseReg, IndexReg, ScaleFactor, offset);
if (R_none != IndexReg) {
IndexOp.type = o_reg;
IndexOp.reg = MDCanonicalizeSubReg((ushort) IndexReg);
IndexOp.dtyp = dt_dword; // Canonical 32-bit width
IndexIter = this->FindUse(IndexOp);
assert(IndexIter != this->GetLastUse());
IndexType = IndexIter->GetType();
}
if (R_none != BaseReg) {
BaseOp.type = o_reg;
BaseOp.reg = MDCanonicalizeSubReg((ushort) BaseReg);
BaseOp.dtyp = dt_dword; // Canonical 32-bit width
BaseIter = this->FindUse(BaseOp);
assert(BaseIter != this->GetLastUse());
BaseType = BaseIter->GetType();
}
if ((R_sp == BaseReg) || (UseFP && (R_bp == BaseReg))) {
if ((R_none != IndexReg) && (!IsNumeric(IndexType))) {
// We have an indexed access into the stack frame.
// Set IndexReg USE type to NUMERIC.
changed = true;
IndexIter = this->SetUseType(IndexOp, NUMERIC);
assert(IndexIter != this->GetLastUse());
}
return changed; // stack accesses will get STACKPTR type in SetImmedTypes()
}
if ((R_sp == IndexReg) || (UseFP && (R_bp == IndexReg))) {
if ((R_none != BaseReg) && (!IsNumeric(BaseType))) {
// We have an indexed access into the stack frame.
// Set BaseReg USE type to NUMERIC.
// Note that BaseReg is really an IndexReg and vice versa.
changed = true;
BaseIter = this->SetUseType(BaseOp, NUMERIC);
assert(BaseIter != this->GetLastUse());
SMP_msg("WARNING: BaseReg is index, IndexReg is base: %s\n",
DisAsmText.GetDisAsm(this->GetAddr()));
}
return changed; // stack accesses will get STACKPTR type in SetImmedTypes()
}
if (IsImmedGlobalAddress(offset)) {
if ((R_none != IndexReg) && (!IsNumeric(IndexType))) {
// We have an indexed access into a global.
// Set IndexReg USE type to NUMERIC.
changed = true;
IndexIter = this->SetUseType(IndexOp, NUMERIC);
assert(IndexIter != this->GetLastUse());
}
if ((R_none != BaseReg) && (!IsNumeric(BaseType))) {
// We have an indexed access into a global.
// Set BaseReg USE type to NUMERIC.
// Note that BaseReg is really an index register.
changed = true;
BaseIter = this->SetUseType(BaseOp, NUMERIC);
assert(BaseIter != this->GetLastUse());
#if SMP_VERBOSE_FIND_POINTERS
SMP_msg("WARNING: BaseReg used as index: %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
return changed; // global immediate is handled in SetImmedTypes()
}
// At this point, we must have a base address in a register, not used
// to directly address the stack or a global.
if ((0 < ScaleFactor) || (R_none == IndexReg)) {
// IndexReg is scaled, meaning it is NUMERIC, so BaseReg must
// be a POINTER; or IndexReg is not present, so BaseReg is the
// only possible holder of an address.
if (R_none != BaseReg) {
if (UNINIT == BaseIter->GetType()) {
changed = true;
BaseIter = this->SetUseType(BaseOp, POINTER);
assert(BaseIter != this->GetLastUse());
}
}
}
else if (R_none == BaseReg) {
// We have an unscaled IndexReg and no BaseReg and offset was
// not a global offset, so IndexReg must be a POINTER.
if (R_none != IndexReg) {
if (UNINIT == IndexType) {
changed = true;
IndexIter = this->SetUseType(IndexOp, POINTER);
assert(IndexIter != this->GetLastUse());
}
}
}
else { // We have BaseReg and an unscaled IndexReg.
// The only hope for typing something like [ebx+edx] is for
// one register to already be typed NUMERIC, in which case
// the other one must be a POINTER, or if one register is
// already POINTER, then the other one must be NUMERIC.
if (IsNumeric(BaseType)) {
if (UNINIT == IndexType) {
// Set to POINTER or PROF_POINTER
changed = true;
IndexIter = this->SetUseType(IndexOp, POINTER);
assert(IndexIter != this->GetLastUse());
}
else if (IsNumeric(IndexType)) {
SMP_msg("ERROR: BaseReg and IndexReg both NUMERIC at %x: %s\n",
this->address, DisAsmText.GetDisAsm(this->GetAddr()));
}
}
else { // BaseReg was not NUMERIC
if (UNINIT == BaseType) { // BaseReg is UNINIT
if (IsNumeric(IndexType)) {
changed = true;
BaseIter = this->SetUseType(BaseOp, POINTER);
assert(BaseIter != this->GetLastUse());
}
else if (IsDataPtr(IndexType)) {
// IndexReg is POINTER, so make BaseReg NUMERIC.
changed = true;
BaseIter = this->SetUseType(BaseOp, NUMERIC);
assert(BaseIter != this->GetLastUse());
}
}
else if (IsDataPtr(BaseType)) {
// BaseReg was a pointer type. IndexReg must be NUMERIC.
if (UNINIT == IndexType) {
changed = true;
IndexIter = this->SetUseType(IndexOp, NUMERIC);
assert(IndexIter != this->GetLastUse());
}
else if (IsDataPtr(IndexType)) {
SMP_msg("ERROR: BaseReg and IndexReg both POINTER at %x: %s\n",
this->address, DisAsmText.GetDisAsm(this->GetAddr()));
}
}
}
}
return changed;
} // end of SMPInstr::MDFindPointerUse()
// Are all DEFs typed to something besides UNINIT?
bool SMPInstr::AllDEFsTyped(void) {
if (this->AreDEFsTyped()) {
return true;
}
bool FoundUNINIT = false;
set<DefOrUse, LessDefUse>::iterator DefIter;
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
if (IsEqType(UNINIT, DefIter->GetType())) {
FoundUNINIT = true;
break;
}
}
if (!FoundUNINIT) {
this->SetDEFsTyped();
}
return (!FoundUNINIT);
} // end of SMPInstr::AllDEFsTyped()
// Are all USEs typed to something besides UNINIT?
bool SMPInstr::AllUSEsTyped(void) {
if (this->AreUSEsTyped()) {
return true;
}
bool FoundUNINIT = false;
set<DefOrUse, LessDefUse>::iterator UseIter;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
if (IsEqType(UNINIT, UseIter->GetType())) {
FoundUNINIT = true;
break;
}
}
if (!FoundUNINIT) {
this->SetUSEsTyped();
}
return (!FoundUNINIT);
} // end of SMPInstr::AllUSEsTyped()
// Return true if UseOp is a USE reg, not just an address reg in a memory USE
bool SMPInstr::IsNonAddressReg(op_t UseOp) const {
bool FoundUse = false;
ushort SearchReg = MDCanonicalizeSubReg(UseOp.reg);
for (size_t OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
op_t Opnd = this->SMPcmd.Operands[OpNum];
if (this->features & UseMacros[OpNum]) { // USE
if (Opnd.type == o_reg) {
ushort TestReg = MDCanonicalizeSubReg(Opnd.reg);
if (TestReg == SearchReg) {
FoundUse = true;
break;
}
}
}
}
return FoundUse;
} // end of SMPInstr::IsNonAddressReg()
uval_t SMPInstr::MDGetShiftCount(void) const {
uval_t ShiftCount = 0;
if (this->MDIsShiftOrRotate()) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
assert(CurrRT->HasRightSubTree());
CurrRT = CurrRT->GetRightTree();
op_t ShiftCountOp = CurrRT->GetRightOperand();
if (o_imm == ShiftCountOp.type) {
ShiftCount = ShiftCountOp.value;
}
}
return ShiftCount;
} // end of SMPInstr::MDGetShiftCount()
// RTL shows DEF operand is subreg.
bool SMPInstr::IsReducedWidthDef(void) const {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
op_t DefOp = CurrRT->GetLeftOperand();
return ((o_void != DefOp.type) && (DefOp.dtyp < 2));
}
// Is a sub-register of UseOp used as a shift counter in the RTL?
// For example, UseOp could be ECX on an x86 machine, and CL
// could be used as a shift or rotate counter.
bool SMPInstr::IsSubRegUsedAsShiftCount(op_t UseOp) {
bool ShiftCounter = false;
if ((o_reg == UseOp.type) && this->MDIsShiftOrRotate()) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
assert(CurrRT->HasRightSubTree());
CurrRT = CurrRT->GetRightTree();
op_t ShiftCountOp = CurrRT->GetRightOperand();
if (o_reg == ShiftCountOp.type) {
ushort UseReg = UseOp.reg;
ushort ShiftCountReg = ShiftCountOp.reg;
ushort WideUseReg = MDCanonicalizeSubReg(UseReg);
ushort WideShiftCountReg = MDCanonicalizeSubReg(ShiftCountReg);
if ((UseReg != ShiftCountReg) && (WideUseReg == WideShiftCountReg)) {
// Registers were not equal, but their canonical enclosing
// registers are equal. Because shift counters that are not
// immediate are the 8-bit subregister in x86 (MD here !!!!!!)
// it must be that the ShiftCountReg is a subreg of UseReg.
// This is the condition we are looking for.
ShiftCounter = true;
}
}
}
return ShiftCounter;
} // end of SMPInstr::IsSubRegUsedAsShiftCount()
// Does UseOp ultimately come from a small positive constant?
bool SMPInstr::IsOpSourceSmallPositiveConstant(op_t UseOp, int UseSSANum) {
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) {
return false;
}
bool FoundSmallConst = false;
bool RegDef = (o_reg == UseOp.type);
bool LocalName = this->GetBlock()->IsLocalName(UseOp);
bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP);
bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized())));
ea_t UseAddr = this->GetAddr();
ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr();
ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName);
bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1));
if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) {
// Try to find in the function level.
UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum);
}
if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame
|| (UseDefAddr == BADADDR) || IndirectMemAccess) {
// Cannot search for general memory DEFs; must be stack or register.
// FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs
// that are LiveIn to the function; pseudo-inst is not a bitwise not.
// First block addr - 1 is pseudo-location that indicates live-in, UpExposed,
// and LocalName means we will not find a DEF anywhere besides this block.
// AboveStackFrame means an incoming arg, whose DEF will not be seen.
FoundSmallConst = false;
}
else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) {
// A block number was returned. That means the DEF is in a Phi Function.
// We could trace all Phi USEs and see if all of them come from small constants
// but we only need one of the Phi USEs to come from
// a small constant to potentially lead to a false positive numeric error. We
// will recurse on all Phi USEs, declaring success if we find a single one of them
// to come from a small constant.
size_t BlockNum = (size_t) UseDefAddr;
assert(!LocalName);
SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum);
assert(NULL != PhiDefBlock);
if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion
set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp);
assert(DefPhiIter != PhiDefBlock->GetLastPhi());
size_t PhiListSize = DefPhiIter->GetPhiListSize();
PhiDefBlock->SetProcessed(true); // Prevent infinite recursion
for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) {
int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex);
if (this->IsOpSourceSmallPositiveConstant(UseOp, PhiUseSSANum)) {
FoundSmallConst = true; // only one success on all Phi USEs is needed
break;
}
}
}
}
else {
bool ValueFound;
uval_t ConstValue;
SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr);
if (DefInst->MDIsSimpleAssignment(ValueFound, ConstValue)) {
FoundSmallConst = (ValueFound && (ConstValue <= 2));
if (!FoundSmallConst && !ValueFound && DefInst->MDIsMoveInstr()) {
// We have a non-immediate move. Trace back through move source to find small const.
op_t CopyUseOp = DefInst->GetMoveSource();
CanonicalizeOpnd(CopyUseOp);
set<DefOrUse, LessDefUse>::iterator UseIter = DefInst->FindUse(CopyUseOp);
assert(UseIter != DefInst->GetLastUse());
int CopyUseSSANum = UseIter->GetSSANum();
FoundSmallConst = DefInst->IsOpSourceSmallPositiveConstant(CopyUseOp, CopyUseSSANum);
}
}
}
return FoundSmallConst;
} // end of SMPInstr::IsOpSourceSmallPositiveConstant()
// Does UseOp ultimately come from a bitwise not instruction?
bool SMPInstr::IsOpSourceBitwiseNot(op_t UseOp, int UseSSANum) {
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) {
return false;
}
bool FoundBitwiseNotInst = false;
bool RegDef = (o_reg == UseOp.type);
bool LocalName = this->GetBlock()->IsLocalName(UseOp);
bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP);
bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized())));
ea_t UseAddr = this->GetAddr();
ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr();
ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName);
bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1));
if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) {
// Try to find in the function level.
UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum);
}
if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame
|| (UseDefAddr == BADADDR) || IndirectMemAccess) {
// Cannot search for general memory DEFs; must be stack or register.
// FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs
// that are LiveIn to the function; pseudo-inst is not a bitwise not.
// First block addr - 1 is pseudo-location that indicates live-in, UpExposed,
// and LocalName means we will not find a DEF anywhere besides this block.
// AboveStackFrame means an incoming arg, whose DEF will not be seen.
FoundBitwiseNotInst = false;
}
else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) {
// A block number was returned. That means the DEF is in a Phi Function.
// We could trace all Phi USEs and see if all of them come from bitwise nots
// but we only need one of the Phi USEs to come from
// a bitwise not to potentially lead to a false positive numeric error. We
// will recurse on all Phi USEs, declaring success if we find a single one of them
// to come from a bitwise not.
size_t BlockNum = (size_t) UseDefAddr;
assert(!LocalName);
SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum);
assert(NULL != PhiDefBlock);
if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion
set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp);
assert(DefPhiIter != PhiDefBlock->GetLastPhi());
size_t PhiListSize = DefPhiIter->GetPhiListSize();
PhiDefBlock->SetProcessed(true); // Prevent infinite recursion
for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) {
int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex);
if (this->IsOpSourceBitwiseNot(UseOp, PhiUseSSANum)) {
FoundBitwiseNotInst = true; // only one success on all Phi USEs is needed
break;
}
}
}
}
else {
SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr);
if (DefInst->MDIsBitwiseNotOpcode()) {
FoundBitwiseNotInst = true;
}
else if (DefInst->MDIsMoveInstr()) {
op_t MoveUseOp = DefInst->GetMoveSource();
if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory
CanonicalizeOpnd(MoveUseOp);
set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp);
assert(MoveUseIter != DefInst->GetLastUse());
int MoveUseSSANum = MoveUseIter->GetSSANum();
FoundBitwiseNotInst = DefInst->IsOpSourceBitwiseNot(MoveUseOp, MoveUseSSANum); // recurse
}
}
else {
// Not a move, not a bitwise not. We must return false.
FoundBitwiseNotInst = false;
}
}
return FoundBitwiseNotInst;
} // end of SMPInstr::IsOpSourceBitwiseNot()
// Does UseOp ultimately come from a set-condition-code instruction?
bool SMPInstr::IsOpSourceConditionCode(op_t UseOp, int UseSSANum) {
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) {
return false;
}
bool FoundConditionalSetInst = false;
bool RegDef = (o_reg == UseOp.type);
bool LocalName = this->GetBlock()->IsLocalName(UseOp);
bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP);
bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized())));
ea_t UseAddr = this->GetAddr();
ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr();
ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName);
bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1));
if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) {
// Try to find in the function level.
UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum);
}
if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame
|| (UseDefAddr == BADADDR) || IndirectMemAccess) {
// Cannot search for general memory DEFs; must be stack or register.
// FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs
// that are LiveIn to the function; pseudo-inst is not a bitwise not.
// First block addr - 1 is pseudo-location that indicates live-in, UpExposed,
// and LocalName means we will not find a DEF anywhere besides this block.
// AboveStackFrame means an incoming arg, whose DEF will not be seen.
FoundConditionalSetInst = false;
}
else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) {
// A block number was returned. That means the DEF is in a Phi Function.
// We could trace all Phi USEs and see if all of them come from condition codes
// but we only need one of the Phi USEs to come from
// a condition code to potentially lead to a false positive numeric error. We
// will recurse on all Phi USEs, declaring success if we find a single one of them
// to come from a condition code.
size_t BlockNum = (size_t) UseDefAddr;
assert(!LocalName);
SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum);
assert(NULL != PhiDefBlock);
if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion
set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp);
assert(DefPhiIter != PhiDefBlock->GetLastPhi());
size_t PhiListSize = DefPhiIter->GetPhiListSize();
PhiDefBlock->SetProcessed(true); // Prevent infinite recursion
for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) {
int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex);
if (this->IsOpSourceConditionCode(UseOp, PhiUseSSANum)) {
FoundConditionalSetInst = true; // only one success on all Phi USEs is needed
break;
}
}
}
}
else {
SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr);
if (DefInst->MDIsAnySetValue()) {
FoundConditionalSetInst = true;
}
else if (DefInst->MDIsMoveInstr()) {
op_t MoveUseOp = DefInst->GetMoveSource();
if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory
CanonicalizeOpnd(MoveUseOp);
set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp);
assert(MoveUseIter != DefInst->GetLastUse());
int MoveUseSSANum = MoveUseIter->GetSSANum();
FoundConditionalSetInst = DefInst->IsOpSourceConditionCode(MoveUseOp, MoveUseSSANum); // recurse
}
}
else {
// Not a move, not a condition code transfer. We must return false.
FoundConditionalSetInst = false;
}
}
return FoundConditionalSetInst;
} // end of SMPInstr::IsOpSourceConditionCode()
// Does UseOp ultimately come from a move-with-zero-extension instruction?
bool SMPInstr::IsOpSourceZeroExtendedMove(op_t UseOp, int UseSSANum, bool TruncationCheck) {
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) {
return false;
}
bool FoundMoveZX = false;
bool RegDef = (o_reg == UseOp.type);
bool LocalName = this->GetBlock()->IsLocalName(UseOp);
bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP);
bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized())));
ea_t UseAddr = this->GetAddr();
ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr();
ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName);
bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1));
if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) {
// Try to find in the function level.
UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum);
}
if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame
|| (UseDefAddr == BADADDR) || IndirectMemAccess) {
// Cannot search for general memory DEFs; must be stack or register.
// FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs
// that are LiveIn to the function; pseudo-inst is not a bitwise not.
// First block addr - 1 is pseudo-location that indicates live-in, UpExposed,
// and LocalName means we will not find a DEF anywhere besides this block.
// AboveStackFrame means an incoming arg, whose DEF will not be seen.
FoundMoveZX = false;
}
else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) {
// A block number was returned. That means the DEF is in a Phi Function.
// We could trace all Phi USEs and see if all of them come from zero-extended
// moves into the UseOp register, but we only need one of the Phi USEs to come from
// a zero-extended move to potentially lead to a false positive numeric error. We
// will recurse on all Phi USEs, declaring success if we find a single one of them
// to come from a zero-extended move.
size_t BlockNum = (size_t) UseDefAddr;
assert(!LocalName);
SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum);
assert(NULL != PhiDefBlock);
if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion
set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp);
assert(DefPhiIter != PhiDefBlock->GetLastPhi());
size_t PhiListSize = DefPhiIter->GetPhiListSize();
PhiDefBlock->SetProcessed(true); // Prevent infinite recursion
for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) {
int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex);
if (this->IsOpSourceZeroExtendedMove(UseOp, PhiUseSSANum, TruncationCheck)) {
FoundMoveZX = true; // only one success on all Phi USEs is needed
break;
}
}
}
}
else {
SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr);
unsigned short SignMask;
if (DefInst->MDIsSignedLoad(SignMask)) {
FoundMoveZX = (FG_MASK_UNSIGNED == SignMask);
}
else if (DefInst->MDIsMoveInstr()) {
op_t MoveUseOp = DefInst->GetMoveSource();
if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory
CanonicalizeOpnd(MoveUseOp);
set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp);
assert(MoveUseIter != DefInst->GetLastUse());
int MoveUseSSANum = MoveUseIter->GetSSANum();
FoundMoveZX = DefInst->IsOpSourceZeroExtendedMove(MoveUseOp, MoveUseSSANum, TruncationCheck); // recurse
}
}
else if (TruncationCheck && DefInst->MDIsNonOverflowingBitManipulation()) {
// Not a move, not a zero-extended move. We must return false for the non-truncation case,
// but we allow non-overflowing bit manipulation instructions in the chain for truncation checks.
// This is because of a benign code pattern:
// reg: = zero-extended move
// reg := reg AND bit pattern
// reg := reg OR bit pattern
// store lower bits of reg
// Compilers like to do 32-bit arithmetic. There was never any good reason otherwise to zero-extend the
// value in the first instruction in the pattern. The lower bits that are stored at the end of the code
// sequence are the only bits that ever mattered, so this is not really a truncation.
set<DefOrUse, LessDefUse>::iterator BitUseIter = DefInst->FindUse(UseOp);
if (BitUseIter != DefInst->GetLastUse()) {
int BitUseSSANum = BitUseIter->GetSSANum();
FoundMoveZX = DefInst->IsOpSourceZeroExtendedMove(UseOp, BitUseSSANum, true); // recurse up the chain
}
}
else {
FoundMoveZX = false;
}
}
return FoundMoveZX;
} // end of SMPInstr::IsOpSourceZeroExtendedMove()
// Does UseOp ultimately come from a move-with-zero-extension instruction OR from a condition code OR from a right shift?
bool SMPInstr::IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(op_t UseOp, int UseSSANum, bool TruncationCheck) {
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) {
return false;
}
bool FoundMoveZXCC = false;
bool RegDef = (o_reg == UseOp.type);
bool LocalName = this->GetBlock()->IsLocalName(UseOp);
bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP);
bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized())));
ea_t UseAddr = this->GetAddr();
ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr();
ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName);
bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1));
if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) {
// Try to find in the function level.
UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum);
}
if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame
|| (UseDefAddr == BADADDR) || IndirectMemAccess) {
// Cannot search for general memory DEFs; must be stack or register.
// FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs
// that are LiveIn to the function; pseudo-inst is not a bitwise not.
// First block addr - 1 is pseudo-location that indicates live-in, UpExposed,
// and LocalName means we will not find a DEF anywhere besides this block.
// AboveStackFrame means an incoming arg, whose DEF will not be seen.
FoundMoveZXCC = false;
}
else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) {
// A block number was returned. That means the DEF is in a Phi Function.
// We could trace all Phi USEs and see if all of them come from zero-extended
// moves into the UseOp register, but we only need one of the Phi USEs to come from
// a zero-extended move to potentially lead to a false positive numeric error. We
// will recurse on all Phi USEs, declaring success if we find a single one of them
// to come from a zero-extended move.
size_t BlockNum = (size_t) UseDefAddr;
assert(!LocalName);
SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum);
assert(NULL != PhiDefBlock);
if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion
set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp);
assert(DefPhiIter != PhiDefBlock->GetLastPhi());
size_t PhiListSize = DefPhiIter->GetPhiListSize();
PhiDefBlock->SetProcessed(true); // Prevent infinite recursion
for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) {
int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex);
if (this->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(UseOp, PhiUseSSANum, TruncationCheck)) {
FoundMoveZXCC = true; // only one success on all Phi USEs is needed
break;
}
}
}
}
else {
SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr);
unsigned short SignMask;
if (DefInst->MDIsSignedLoad(SignMask)) {
FoundMoveZXCC = (FG_MASK_UNSIGNED == SignMask);
}
else if (DefInst->MDIsAnySetValue() || DefInst->MDIsShiftRight()) {
FoundMoveZXCC = true;
}
else if (DefInst->MDIsMoveInstr()) {
op_t MoveUseOp = DefInst->GetMoveSource();
if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory
CanonicalizeOpnd(MoveUseOp);
set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp);
assert(MoveUseIter != DefInst->GetLastUse());
int MoveUseSSANum = MoveUseIter->GetSSANum();
FoundMoveZXCC = DefInst->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(MoveUseOp, MoveUseSSANum, TruncationCheck); // recurse
}
}
else if (TruncationCheck && (DefInst->MDIsNonOverflowingBitManipulation() || DefInst->MDIsSmallAdditionOrSubtraction())) {
// Not a move, not a zero-extended move. We must return false for the non-truncation case,
// but we allow non-overflowing bit manipulation instructions in the chain for truncation checks.
// This is because of a benign code pattern:
// reg: = zero-extended move
// reg := reg AND bit pattern
// reg := reg OR bit pattern
// store lower bits of reg
// Compilers like to do 32-bit arithmetic. There was never any good reason otherwise to zero-extend the
// value in the first instruction in the pattern. The lower bits that are stored at the end of the code
// sequence are the only bits that ever mattered, so this is not really a truncation.
// NOTE: We combine into this case additions or subtractions of small values, as they only operate on the
// lower bits of the register.
set<DefOrUse, LessDefUse>::iterator BitUseIter = DefInst->FindUse(UseOp);
if (BitUseIter != DefInst->GetLastUse()) {
int BitUseSSANum = BitUseIter->GetSSANum();
FoundMoveZXCC = DefInst->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(UseOp, BitUseSSANum, true); // recurse up the chain
}
}
else {
FoundMoveZXCC = false;
}
}
return FoundMoveZXCC;
} // end of SMPInstr::IsOpSourceZeroExtendedMoveShiftRightOrConditionCode()
// Trace through moves to any of the above cases, return source inst
bool SMPInstr::IsOpSourceSpecial(op_t UseOp, int UseSSANum, bool TruncationCheck, ea_t &SourceInstAddr) {
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if ((UseSSANum == -1) || (!MDIsDataFlowOpnd(UseOp, UseFP))) {
return false;
}
bool FoundMoveNotZXCCSubreg = false;
bool RegDef = (o_reg == UseOp.type);
bool LocalName = this->GetBlock()->IsLocalName(UseOp);
bool IndirectMemAccess = MDIsIndirectMemoryOpnd(UseOp, UseFP);
bool AboveStackFrame = (!RegDef && !IndirectMemAccess && (this->GetBlock()->GetFunc()->WritesAboveLocalFrame(UseOp, this->AreDefsNormalized())));
ea_t UseAddr = this->GetAddr();
ea_t FirstFuncAddr = this->GetBlock()->GetFunc()->GetFirstFuncAddr();
ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName);
bool UpExposedUse = (UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1));
if (!LocalName && !AboveStackFrame && !IndirectMemAccess && ((UseDefAddr == BADADDR) || UpExposedUse)) {
// Try to find in the function level.
UseDefAddr = this->GetBlock()->GetFunc()->GetGlobalDefAddr(UseOp, UseSSANum);
}
if ((UseDefAddr == (FirstFuncAddr - 1)) || AboveStackFrame
|| (UseDefAddr == BADADDR) || IndirectMemAccess) {
// Cannot search for general memory DEFs; must be stack or register.
// FirstFuncAddr - 1 signifies the pseudo-inst to hold DEFs of regs
// that are LiveIn to the function; pseudo-inst is not a bitwise not.
// First block addr - 1 is pseudo-location that indicates live-in, UpExposed,
// and LocalName means we will not find a DEF anywhere besides this block.
// AboveStackFrame means an incoming arg, whose DEF will not be seen.
FoundMoveNotZXCCSubreg = false;
}
else if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) {
// A block number was returned. That means the DEF is in a Phi Function.
// We could trace all Phi USEs and see if all of them come from zero-extended
// moves into the UseOp register, but we only need one of the Phi USEs to come from
// a zero-extended move to potentially lead to a false positive numeric error. We
// will recurse on all Phi USEs, declaring success if we find a single one of them
// to come from a zero-extended move.
size_t BlockNum = (size_t) UseDefAddr;
assert(!LocalName);
SMPBasicBlock *PhiDefBlock = this->GetBlock()->GetFunc()->GetBlockByNum(BlockNum);
assert(NULL != PhiDefBlock);
if (!PhiDefBlock->IsProcessed()) { // Prevent infinite recursion
set<SMPPhiFunction, LessPhi>::iterator DefPhiIter = PhiDefBlock->FindPhi(UseOp);
assert(DefPhiIter != PhiDefBlock->GetLastPhi());
size_t PhiListSize = DefPhiIter->GetPhiListSize();
PhiDefBlock->SetProcessed(true); // Prevent infinite recursion
for (size_t UseIndex = 0; UseIndex < PhiListSize; ++UseIndex) {
int PhiUseSSANum = DefPhiIter->GetUseSSANum(UseIndex);
if (this->IsOpSourceSpecial(UseOp, PhiUseSSANum, TruncationCheck, SourceInstAddr)) {
FoundMoveNotZXCCSubreg = true; // only one success on all Phi USEs is needed
break;
}
}
}
}
else {
SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr);
unsigned short SignMask;
if (DefInst->MDIsSignedLoad(SignMask)) {
FoundMoveNotZXCCSubreg = ((FG_MASK_UNSIGNED == SignMask) || TruncationCheck); // Truncation chain cares if source was reduced width.
if (FoundMoveNotZXCCSubreg) {
SourceInstAddr = DefInst->GetAddr();
}
}
else if (DefInst->MDIsAnySetValue() || DefInst->MDIsShiftRight() || DefInst->MDIsBitwiseNotOpcode()) {
FoundMoveNotZXCCSubreg = true;
SourceInstAddr = DefInst->GetAddr();
}
else if (DefInst->MDIsMoveInstr()) {
op_t MoveUseOp = DefInst->GetMoveSource();
if (MDIsDataFlowOpnd(MoveUseOp, UseFP)) { // pattern is simple; don't try to follow through non-stack memory
CanonicalizeOpnd(MoveUseOp);
set<DefOrUse, LessDefUse>::iterator MoveUseIter = DefInst->FindUse(MoveUseOp);
assert(MoveUseIter != DefInst->GetLastUse());
int MoveUseSSANum = MoveUseIter->GetSSANum();
FoundMoveNotZXCCSubreg = DefInst->IsOpSourceSpecial(MoveUseOp, MoveUseSSANum, TruncationCheck, SourceInstAddr); // recurse
}
}
else if (TruncationCheck && (DefInst->MDIsNonOverflowingBitManipulation() || DefInst->MDIsSmallAdditionOrSubtraction())) {
// Not a move, not a zero-extended move. We must return false for the non-truncation case,
// but we allow non-overflowing bit manipulation instructions in the chain for truncation checks.
// This is because of a benign code pattern:
// reg: = zero-extended move
// reg := reg AND bit pattern
// reg := reg OR bit pattern
// store lower bits of reg
// Compilers like to do 32-bit arithmetic. There was never any good reason otherwise to zero-extend the
// value in the first instruction in the pattern. The lower bits that are stored at the end of the code
// sequence are the only bits that ever mattered, so this is not really a truncation.
// NOTE: We combine into this case additions or subtractions of small values, as they only operate on the
// lower bits of the register.
set<DefOrUse, LessDefUse>::iterator BitUseIter = DefInst->FindUse(UseOp);
if (BitUseIter != DefInst->GetLastUse()) {
int BitUseSSANum = BitUseIter->GetSSANum();
FoundMoveNotZXCCSubreg = DefInst->IsOpSourceSpecial(UseOp, BitUseSSANum, true, SourceInstAddr); // recurse up the chain
}
}
else if (TruncationCheck) {
// Check for reduced-width arithmetic.
if (DefInst->IsReducedWidthDef()) { // chain like sub cl,3; mov eax,ecx; then "truncating" move of al
FoundMoveNotZXCCSubreg = true;
SourceInstAddr = DefInst->GetAddr();
}
}
}
return FoundMoveNotZXCCSubreg;
} // end of SMPInstr::IsOpSourceSpecial()
// Is opcode a shift or rotate?
// NOTE: We omit MMX/SSE unit shifts that do not use a general purpose
// register as a shift counter, because right now this method is only
// used as a helper for IsSubRegUsedAsShiftCount().
bool SMPInstr::MDIsShiftOrRotate(void) const {
return (((NN_rcl <= SMPcmd.itype) && (NN_ror >= SMPcmd.itype))
|| ((NN_sal <= SMPcmd.itype) && (NN_shr >= SMPcmd.itype))
|| (NN_shld == SMPcmd.itype) || (NN_shrd == SMPcmd.itype));
} // end of SMPInstr::MDIsShiftOrRotate()
// Is opcode a shift to the right?
bool SMPInstr::MDIsShiftRight(void) const {
return ((NN_sar == SMPcmd.itype) || (NN_shr == SMPcmd.itype));
}
// Does the shift or rotate RTL move the upper HalfBitWidth bits
// into the lower half of the register? Or, if MustBeHalfRegWidth is false,
// do we shift right by HalfBitWidth bits?
bool SMPInstr::ShiftMakesUpperBitsLower(size_t HalfBitWidth, bool MustBeHalfRegWidth) {
bool FullCircle = false;
if (!MustBeHalfRegWidth || (MD_NORMAL_MACHINE_BITWIDTH == (HalfBitWidth * 2))) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
if ((NULL != CurrRT) && (CurrRT->HasRightSubTree())) {
CurrRT = CurrRT->GetRightTree();
SMPoperator CurrOper = CurrRT->GetOperator();
bool LeftRotate = (SMP_ROTATE_LEFT == CurrOper);
if ((SMP_U_RIGHT_SHIFT == CurrOper) || (SMP_S_RIGHT_SHIFT == CurrOper)
|| LeftRotate || (SMP_ROTATE_RIGHT == CurrOper)) {
if (CurrRT->HasRightSubTree()) { // double-word shift
CurrRT = CurrRT->GetRightTree();
}
assert(!(CurrRT->HasRightSubTree()));
op_t ShiftCount = CurrRT->GetRightOperand();
if (o_imm == ShiftCount.type) {
uval_t ImmVal = ShiftCount.value;
// If we rotate left by e.g. 32-HalfBitWidth bits, then we are processing
// bytes or halfregs one at a time; if we rotate or shift right by HalfBitWidth,
// we are processing the register one HalfBitWidth at a time. We also a
if (MustBeHalfRegWidth || (!LeftRotate)) {
FullCircle = (HalfBitWidth == ImmVal);
}
else {
// Left rotate amount plus HalfBitWidth must add up to full register width
FullCircle = (MD_NORMAL_MACHINE_BITWIDTH == (ImmVal + HalfBitWidth));
}
}
}
}
}
return FullCircle;
} // SMPInstr::ShiftMakesUpperBitsLower()
#if 0
// Find SearchDelta in StackDeltaSet, inserting it if not found. Return whether it was initially found.
bool SMPInstr::FindStackPtrDelta(sval_t SearchDelta) const {
bool found = (this->StackDeltaSet.find(SearchDelta) != this->StackDeltaSet.end());
if (!found) {
this->StackDeltaSet.insert(SearchDelta);
if (SearchDelta < this->StackPtrOffset) {
// Mimic IDA Pro, which seems to keep the biggest stack frame possible.
// With negative stack deltas, this means the smallest stack delta is kept.
this->SetStackPtrOffset(SearchDelta);
}
}
return found;
} // end of SMPInstr::FindStackPtrDelta()
#endif
// Set the type of all immediate operands found in the USE set.
// Set all flags and floating point register USEs and DEFs to NUMERIC also,
// along with easily determined types for special cases.
void SMPInstr::SetImmedTypes(bool UseFP) {
set<DefOrUse, LessDefUse>::iterator CurrUse;
set<DefOrUse, LessDefUse>::iterator CurrDef;
op_t UseOp;
op_t DefOp;
uval_t ImmVal;
bool DebugFlag = false;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
DebugFlag = DebugFlag || (this->address == 0x805cd52) || (this->address == 0x805cd56);
DebugFlag |= (0 == strncmp("__libc_csu_fini", this->BasicBlock->GetFunc()->GetFuncName(), 15));
#endif
CurrUse = this->GetFirstUse();
while (CurrUse != this->GetLastUse()) {
UseOp = CurrUse->GetOp();
if (DebugFlag) {
SMP_msg("SetImmedTypes USE: ");
PrintOperand(UseOp);
SMP_msg("\n");
}
if (o_imm == UseOp.type) {
ImmVal = UseOp.value;
if (IsImmedGlobalAddress((ea_t) ImmVal)) {
if (DebugFlag) SMP_msg("Setting to GLOBALPTR\n");
CurrUse = this->SetUseType(UseOp, GLOBALPTR);
}
#if 0
else if (IsDataAddress((ea_t) ImmVal)) {
// NOTE: We must call IsDataAddress() before we call IsImmedCodeAddress()
// to catch the data addresses within the code address range.
if (DebugFlag) SMP_msg("Setting to POINTER\n");
CurrUse = this->SetUseType(UseOp, POINTER);
}
#endif
else if (this->MDIsInterruptCall() || IsImmedCodeAddress((ea_t) ImmVal)) {
if (DebugFlag) SMP_msg("Setting to CODEPTR\n");
CurrUse = this->SetUseType(UseOp, CODEPTR);
}
else { // NUMERIC
if (DebugFlag) SMP_msg("Setting to NUMERIC\n");
CurrUse = this->SetUseType(UseOp, NUMERIC);
}
}
else if (o_reg == UseOp.type) {
if (UseOp.is_reg(X86_FLAGS_REG)) {
if (DebugFlag) SMP_msg("Setting flags reg to NUMERIC\n");
CurrUse = this->SetUseType(UseOp, NUMERIC);
}
#if 1
else if (UseOp.is_reg(R_sp) || (UseFP && UseOp.is_reg(R_bp))) {
if (DebugFlag) SMP_msg("Setting reg to STACKPTR\n");
CurrUse = this->SetUseType(UseOp, STACKPTR);
}
#endif
}
#if 0 // could these registers have pointers in them?
else if ((o_trreg == UseOp.type) ||(o_dbreg == UseOp.type) || (o_crreg == UseOp.type)) {
if (DebugFlag) SMP_msg("Setting special reg to NUMERIC\n");
CurrUse = this->SetUseType(UseOp, NUMERIC);
}
#endif
else if ((o_fpreg == UseOp.type) || (o_mmxreg == UseOp.type) || (o_xmmreg == UseOp.type)) {
if (DebugFlag) SMP_msg("Setting floating point reg to NUMERIC\n");
CurrUse = this->SetUseType(UseOp, NUMERIC);
}
else if ((o_mem == UseOp.type) || (o_phrase == UseOp.type) || (o_displ == UseOp.type)) {
// For memory operands, we need to identify the POINTER value that
// is used in the addressing mode, if possible.
(void) this->MDFindPointerUse(UseOp, UseFP);
}
++CurrUse;
} // end while all USEs via CurrUse
CurrDef = this->GetFirstDef();
while (CurrDef != this->GetLastDef()) {
DefOp = CurrDef->GetOp();
if (DebugFlag) {
SMP_msg("SetImmedTypes DEF: ");
PrintOperand(DefOp);
SMP_msg("\n");
}
if (DebugFlag) SMP_msg("FuncName: %s\n", this->BasicBlock->GetFunc()->GetFuncName());
if (o_reg == DefOp.type) {
if (DefOp.is_reg(X86_FLAGS_REG)) {
if (DebugFlag) SMP_msg("Setting flags reg DEF to NUMERIC\n");
CurrDef = this->SetDefType(DefOp, NUMERIC);
// No need to propagate this DEF type, as all flags will become NUMERIC.
}
#if 1
else if (DefOp.is_reg(R_sp) || (DefOp.is_reg(R_bp) && UseFP)) {
if (DebugFlag) SMP_msg("Setting reg DEF to STACKPTR\n");
CurrDef = this->SetDefType(DefOp, STACKPTR);
assert(CurrDef != this->Defs.GetLastRef());
// No need to propagate; all stack and frame pointers will become STACKPTR.
}
#endif
}
else if ((o_fpreg == DefOp.type) || (o_mmxreg == DefOp.type) || (o_xmmreg == DefOp.type)) {
if (DebugFlag) SMP_msg("Setting floating point reg DEF to NUMERIC\n");
CurrDef = this->SetDefType(DefOp, NUMERIC);
// No need to propagate; all FP reg uses will become NUMERIC anyway.
}
#if 0 // could these registers have pointers in them?
else if ((o_trreg == DefOp.type) || (o_dbreg == DefOp.type) || (o_crreg == DefOp.type)) {
if (DebugFlag) SMP_msg("Setting special reg DEF to NUMERIC\n");
CurrDef = this->SetDefType(DefOp, NUMERIC);
}
#endif
else if ((o_mem == DefOp.type) || (o_phrase == DefOp.type) || (o_displ == DefOp.type)) {
// For memory operands, we need to identify the POINTER value that
// is used in the addressing mode, if possible.
(void) this->MDFindPointerUse(DefOp, UseFP);
}
++CurrDef;
} // end while all DEFs via CurrDef
return;
} // end of SMPInstr::SetImmedTypes()
// Is the instruction a load from the stack?
void SMPInstr::MDFindLoadFromStack(bool UseFP) {
set<DefOrUse, LessDefUse>::iterator UseIter;
op_t UseOp;
if ((3 == this->OptType) && (this->HasSourceMemoryOperand())) {
// Loads and stores are OptCategory 3. We want only loads from the stack.
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseOp = UseIter->GetOp();
if (MDIsStackAccessOpnd(UseOp, UseFP)) {
this->SetLoadFromStack();
break;
}
}
}
return;
} // end of SMPInstr::MDFindLoadFromStack()
// Determine if instr is inherently signed load instruction.
// True if sign or zero-extended; pass out mask bits if true.
bool SMPInstr::MDIsSignedLoad(unsigned short &SignMask) {
unsigned short opcode = this->SMPcmd.itype;
if (NN_movzx == opcode) {
SignMask = FG_MASK_UNSIGNED;
}
else if (NN_movsx == opcode) {
SignMask = FG_MASK_SIGNED;
}
else {
return false;
}
return true;
}
// true if increment or addition of small positive immediate value
#define STARS_SMALL_POS_VALUE_LIMIT 16
bool SMPInstr::MDIsSmallPositiveAddition(void) {
unsigned short opcode = this->SMPcmd.itype;
bool found = (NN_inc == opcode);
uval_t ImmVal;
if (!found && ((NN_add == opcode) || (NN_adc == opcode))) {
set<DefOrUse, LessDefUse>::iterator UseIter;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
op_t UseOp = UseIter->GetOp();
if (o_imm == UseOp.type) {
ImmVal = UseOp.value;
if ((ImmVal <= STARS_SMALL_POS_VALUE_LIMIT) && (0 < ImmVal)) {
found = true;
break;
}
}
}
}
else if (this->MDIsLoadEffectiveAddressInstr()) {
// See if we have the form: lea reg1,[reg2+const]
SMPRegTransfer *CurrRT = this->RTL.GetRT(0);
if (CurrRT->HasRightSubTree()) {
CurrRT = CurrRT->GetRightTree();
SMPoperator CurrOp = CurrRT->GetOperator();
if ((SMP_ADD == CurrOp) && (!CurrRT->HasRightSubTree())) {
op_t RightOp = CurrRT->GetRightOperand();
if (o_imm == RightOp.type) {
ImmVal = RightOp.value;
if ((ImmVal <= STARS_SMALL_POS_VALUE_LIMIT) && (0 < ImmVal)) {
found = true;
}
}
}
}
}
return found;
} // end of SMPInstr::MDIsSmallPositiveAddition()
// true if increment, decrement, or addition or subtraction of small immediate value
bool SMPInstr::MDIsSmallAdditionOrSubtraction(void) {
unsigned short opcode = this->SMPcmd.itype;
bool found = ((NN_inc == opcode) || (NN_dec == opcode));
if ((NN_add == opcode) || (NN_adc == opcode) || (NN_sub == opcode) || (NN_sbb == opcode)) {
set<DefOrUse, LessDefUse>::iterator UseIter;
for (UseIter = this->GetFirstUse(); !found && (UseIter != this->GetLastUse()); ++UseIter) {
op_t UseOp = UseIter->GetOp();
if (o_imm == UseOp.type) {
uval_t ImmVal = UseOp.value;
int SignedImmVal = (int) ImmVal;
found = ((SignedImmVal >= (-STARS_SMALL_POS_VALUE_LIMIT)) && (SignedImmVal <= STARS_SMALL_POS_VALUE_LIMIT));
}
}
}
return found;
} // end of SMPInstr::MDIsSmallAdditionOrSubtraction()
// Inst is move or register clear.
bool SMPInstr::MDIsSimpleAssignment(bool &ValueFound, uval_t &ConstValue) {
bool Simple = false;
ValueFound = false;
if (this->IsRegClearIdiom()) {
Simple = true;
ValueFound = true;
ConstValue = 0;
}
else if (this->MDIsMoveInstr()) {
Simple = true;
if (o_imm == this->MoveSource.type) {
ValueFound = true;
ConstValue = this->MoveSource.value;
}
}
return Simple;
} // end of SMPInstr::MDIsSimpleAssignment()
// Inst clears register or adds or subtracts small immediate value, as is done with counter variables.
bool SMPInstr::IsCounterOperation(void) {
bool CounterOperation = false;
bool ImmedValueFound = false;
uval_t ConstValue = 1;
if (this->MDIsSimpleAssignment(ImmedValueFound, ConstValue)) {
CounterOperation = (ImmedValueFound && (0 == ConstValue));
}
else {
CounterOperation = this->MDIsSmallAdditionOrSubtraction();
}
return CounterOperation;
} // end of SMPInstr::IsCounterOperation()
// Inst does an AND, OR, XOR operation; does not do add, subtract, etc.
bool SMPInstr::MDIsNonOverflowingBitManipulation(void) const {
unsigned short opcode = this->SMPcmd.itype;
return ((NN_and == opcode) || (NN_or == opcode) || (NN_shl == opcode)
|| ((NN_xor == opcode) && !this->IsRegClearIdiom()));
} // end of SMPInstr::MDIsNonOverflowingBitManipulation()
// return true if traced USE to a constant value
bool SMPInstr::FindConstantValue(set<DefOrUse, LessDefUse>::iterator UseIter, uval_t &ConstValue) {
bool FoundConst = false;
assert(UseIter != this->GetLastUse());
op_t UseOp = UseIter->GetOp();
if (UseOp.type == o_imm) {
ConstValue = UseOp.value;
FoundConst = true;
}
else {
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if ((o_reg == UseOp.type)
|| (MDIsStackAccessOpnd(UseOp, UseFP) && !MDIsIndirectMemoryOpnd(UseOp, UseFP))) {
// We can trace registers and stack locations through SSA chains, if the stack location is directly accessed.
int UseSSANum = UseIter->GetSSANum();
bool LocalName = this->GetBlock()->IsLocalName(UseOp);
ea_t UseAddr = this->GetAddr();
ea_t UseDefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, UseAddr, UseSSANum, LocalName);
if (UseDefAddr < this->GetBlock()->GetFunc()->GetNumBlocks()) {
// A block number was returned. That means the DEF is in a Phi Function.
// We could trace all Phi USEs and see if all of them come from a single
// constant value, but this is highly unlikely. Terminate search.
;
}
else if ((UseDefAddr == (this->GetBlock()->GetFirstAddr() - 1)) || (BADADDR == UseDefAddr)) {
// The DEF is in a Phi function in the current block, or is BADADDR. Terminate search.
;
}
else {
SMPInstr *DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(UseDefAddr);
bool ValueFound = false;
if (DefInst->MDIsSimpleAssignment(ValueFound, ConstValue)) {
if (ValueFound) {
FoundConst = true; // ConstValue holds the value that was found.
}
else {
set<DefOrUse, LessDefUse>::iterator NewUseIter = DefInst->GetFirstUse();
// A simple assignment should have only one USE. Recurse on that USE.
FoundConst = DefInst->FindConstantValue(NewUseIter, ConstValue);
}
}
}
}
}
return FoundConst;
} // end of SMPInstr::FindConstantValue()
// Infer sign, bit width, other type info for simple cases where all the info needed is
// within the instruction or can be read from the FineGrainedStackTable in the SMPFunction.
// NOTE: Must be called after SSA analysis is complete.
void SMPInstr::MDSetWidthSignInfo(bool UseFP) {
set<DefOrUse, LessDefUse>::iterator UseIter;
set<DefOrUse, LessDefUse>::iterator DefIter;
op_t UseOp, DefOp;
struct FineGrainedInfo FGEntry;
unsigned short SignMask, TempSign, WidthMask;
int DefHashValue, UseHashValue;
ea_t DefAddr; // for flags USE in conditional set
int SSANum; // for flags USE in conditional set
bool LocalFlags; // is flags register a local name?
bool case1, case2, case3, case4, case5, case6, case7, case8;
bool SignedSetOpcode = this->MDIsSignedSetValue();
bool UnsignedSetOpcode = this->MDIsUnsignedSetValue();
case1 = this->IsLoadFromStack();
case2 = this->MDIsSignedLoad(SignMask); // sets value of SignMask if it returns true
case3 = (7 == this->OptType); // Multiplies and divides
case4 = ((CALL == this->GetDataFlowType()) || (INDIR_CALL == this->GetDataFlowType()));
case5 = (SignedSetOpcode || UnsignedSetOpcode); // set boolean based on flag condition
case6 = this->MDDoublesWidth(); // convert byte to word, word to dword, etc.
case7 = this->MDAlwaysUnsignedDEF();
case8 = this->MDAlwaysSignedDEF();
// Case 1: Load from stack location.
if (case1) {
bool success = false;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseOp = UseIter->GetOp();
if (MDIsStackAccessOpnd(UseOp, UseFP)) {
// Found the stack location being loaded into a register. Now we need
// to get the sign and width info from the fine grained stack frame
// analysis.
success = this->GetBlock()->GetFunc()->MDGetFGStackLocInfo(this->address, UseOp, FGEntry);
assert(success);
// Now we have signedness info in FGEntry. We need to OR it into the register target of the load.
if (FGEntry.SignMiscInfo == 0)
break; // nothing to OR in; save time
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
DefOp = DefIter->GetOp();
if (o_reg == DefOp.type) {
CanonicalizeOpnd(DefOp);
TempSign = FGEntry.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS; // Get both sign bit flags
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
if (this->BasicBlock->IsLocalName(DefOp)) {
this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, TempSign);
}
else {
this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, TempSign);
}
break; // Should be only one register target for stack load, and no flags are set.
}
}
break; // Only concerned with the stack operand
}
}
assert(success);
} // end if this->IsLoadFromStack()
// Case 2: Loads that are sign-extended or zero-extended imply signed and unsigned, respectively.
// NOTE: If from the stack, they were handled in Case 1, and the signedness of the stack location
// was recorded a long time ago in SMPFunction::FindOutgoingArgsSize();
else if (case2) {
DefIter = this->GetFirstDef();
while (DefIter != this->GetLastDef()) {
// All non-memory DEFs besides the flags register should get the new SignMask ORed in.
// On x86, there should only be one DEF for this move, and no flags, but we will generalize
// in case other architectures are odd.
DefOp = DefIter->GetOp();
if (!(IsMemOperand(DefOp) || MDIsFlagsReg(DefOp))) {
CanonicalizeOpnd(DefOp);
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
if (this->BasicBlock->IsLocalName(DefOp)) {
this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, SignMask);
}
else {
this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask);
}
}
++DefIter;
}
// If the signed load is from memory, the only USEs are the memory
// operand and addressing registers. We do not want to claim that
// EBX is signed in the instruction movsx eax,[ebx]. Only the DEF
// register EAX and the memory location [EBX] are signed, and we
// have no idea where [EBX] is, so we punt on all USEs if we have
// a memory source operand.
if (!(this->HasSourceMemoryOperand())) {
UseIter = this->GetFirstUse();
while (UseIter != this->GetLastUse()) {
// All non-memory USEs besides the flags register should get the new SignMask ORed in.
UseOp = UseIter->GetOp();
if (!(IsMemOperand(UseOp) || MDIsFlagsReg(UseOp))) {
CanonicalizeOpnd(UseOp);
UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(UseOp)) {
this->BasicBlock->UpdateUseSignMiscInfo(UseHashValue, SignMask);
}
else {
this->BasicBlock->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, SignMask);
}
}
++UseIter;
}
}
} // end of case 2
// Case 3: multiplies and divides can be signed or unsigned.
else if (case3) { // Multiplies and divides are type 7.
if (this->MDIsSignedArithmetic()) {
SignMask = FG_MASK_SIGNED;
}
else if (this->MDIsUnsignedArithmetic()) {
SignMask = FG_MASK_UNSIGNED;
}
else {
SignMask = 0; // unknown, uninitialized
}
if (0 != SignMask) {
DefIter = this->GetFirstDef();
while (DefIter != this->GetLastDef()) {
// All DEFs besides the flags register should get the new SignMask ORed in.
DefOp = DefIter->GetOp();
if ((DefOp.type == o_reg) && (!(DefOp.is_reg(X86_FLAGS_REG)))) {
CanonicalizeOpnd(DefOp);
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
if (this->BasicBlock->IsLocalName(DefOp)) {
this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, SignMask);
}
else {
this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask);
}
}
++DefIter;
}
UseIter = this->GetFirstUse();
while (UseIter != this->GetLastUse()) {
// All USEs besides the flags register should get the new SignMask ORed in.
UseOp = UseIter->GetOp();
if ((UseOp.type == o_reg) && (!(UseOp.is_reg(X86_FLAGS_REG)))) {
CanonicalizeOpnd(UseOp);
UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(UseOp)) {
this->BasicBlock->UpdateUseSignMiscInfo(UseHashValue, SignMask);
}
else {
this->BasicBlock->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, SignMask);
}
}
++UseIter;
}
} // end if (0 != SignMask)
} // end of case 3 (multiplies and divides)
// Case 4: Calls to library functions can reveal the type of the return register.
else if (case4) {
// Get name of function called.
string FuncName = this->GetTrimmedCalledFunctionName();
// Get FG info, if any, for called function.
GetLibFuncFGInfo(FuncName, FGEntry);
// See if anything was returned in FGEntry.
if ((FGEntry.SignMiscInfo != 0) || (FGEntry.SizeInfo != 0)) {
// Need to update the FG info for the DEF of the return register.
DefOp = InitOp;
DefOp.type = o_reg;
DefOp.reg = MD_RETURN_VALUE_REG;
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
if (this->BasicBlock->IsLocalName(DefOp)) {
this->BasicBlock->UpdateDefFGInfo(DefHashValue, FGEntry);
}
else {
this->BasicBlock->GetFunc()->UpdateDefFGInfo(DefHashValue, FGEntry);
}
}
// See if we make a call to a library function that needs signedness checks on
// unsigned incoming args.
unsigned int ArgPosBits = 0;
GetUnsignedArgPositionsForCallName(FuncName, ArgPosBits);
if (0 < ArgPosBits) {
// Mark our basic block as containing this type of call, so signedness checks
// can be done later.
this->GetBlock()->SetCallsUnsignedArgFunc();
// Find the argument assignments and mark them, to trigger later signedness checks.
this->GetBlock()->MarkUnsignedArgs(this->GetAddr(), ArgPosBits);
}
} // end of case4 (function calls)
else if (case5) { // signed or unsigned conditional set opcode
if (UnsignedSetOpcode) {
SignMask = FG_MASK_UNSIGNED;
}
else {
assert(SignedSetOpcode);
SignMask = FG_MASK_SIGNED;
}
// Find the flags USE.
UseOp.type = o_reg; // set up a dummy op for searching
UseOp.reg = X86_FLAGS_REG;
UseIter = this->FindUse(UseOp);
assert(UseIter != this->GetLastUse());
UseOp = UseIter->GetOp(); // get full info in all fields of UseOp
SSANum = UseIter->GetSSANum();
LocalFlags = this->GetBlock()->IsLocalName(UseOp);
DefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, this->GetAddr(), SSANum, LocalFlags);
// Pass DefAddr to recursive helper function to propagate signedness of the set opcode.
this->GetBlock()->PropagateBranchSignedness(DefAddr, UseOp, SignMask);
}
else if (case6) { // sign extend to double the width of USE operand into DEF operand
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
DefOp = DefIter->GetOp();
assert(o_reg == DefOp.type);
SSANum = DefIter->GetSSANum();
DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum);
UseIter = this->GetFirstUse();
assert(UseIter != this->GetLastUse());
UseOp = UseIter->GetOp();
assert(o_reg == UseOp.type);
assert(UseOp.reg == DefOp.reg);
UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum());
SignMask = FG_MASK_SIGNED; // opcodes do sign extension => signed
// Mark DEF and USE as signed.
if (this->GetBlock()->IsLocalName(DefOp)) {
this->GetBlock()->UpdateDefSignMiscInfo(DefHashValue, SignMask);
this->GetBlock()->UpdateUseSignMiscInfo(UseHashValue, SignMask);
}
else {
this->GetBlock()->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask);
this->GetBlock()->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, SignMask);
}
}
else if (case7 || case8) { // always unsigned DEF or always signed DEF.
DefIter = this->GetFirstNonFlagsDef();
if (DefIter != this->GetLastDef()) {
DefOp = DefIter->GetOp();
if (o_reg == DefOp.type) {
SSANum = DefIter->GetSSANum();
DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum);
if (case7)
SignMask = FG_MASK_UNSIGNED;
else
SignMask = FG_MASK_SIGNED;
// Mark DEF as signed or unsigned.
if (this->GetBlock()->IsLocalName(DefOp)) {
this->GetBlock()->UpdateDefSignMiscInfo(DefHashValue, SignMask);
}
else {
this->GetBlock()->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, SignMask);
}
}
}
}
// For all register DEFs and USEs, we should get the obvious register width info
// updated. Need to use the RTL operands to get accurate widths.
SMPRegTransfer *CurrRT;
for (size_t index = 0; index < this->RTL.GetCount(); ++index) {
CurrRT = this->RTL.GetRT(index);
DefOp = CurrRT->GetLeftOperand();
// Avoid setting def width for case 2; we leave it as zero so that
// later uses can determine whether the zero-extension or sign-extension
// bits ever got used. See more discussion in EmitIntegerErrorAnnotations()
// for the CHECK TRUNCATION case.
// NOTE: case2 can be set to true even in the case1/case2 overlap case that
// only passes through the case1 code above. This is intentional. We want
// to leave the DEF width set to 0 for all of case2 including the case1 overlap.
if (!case2) {
if (MDIsGeneralPurposeReg(DefOp)) {
WidthMask = ComputeOperandBitWidthMask(DefOp, 0);
CanonicalizeOpnd(DefOp);
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
if (this->BasicBlock->IsLocalName(DefOp)) {
this->BasicBlock->UpdateDefWidthTypeInfo(DefHashValue, WidthMask);
}
else {
this->BasicBlock->GetFunc()->UpdateDefWidthTypeInfo(DefHashValue, WidthMask);
}
}
}
if (CurrRT->HasRightSubTree()) {
this->MDSetRTLRegWidthInfo(CurrRT->GetRightTree());
}
else {
UseOp = CurrRT->GetRightOperand();
this->SetRTLUseOpRegWidthInfo(UseOp);
}
} // end for all RTLs
return;
} // end of SMPInstr::MDSetWidthSignInfo()
// Infer sign from the SMP types for USEs and DEFs.
void SMPInstr::InferSignednessFromSMPTypes(bool UseFP) {
// Start with registers only, infer that all kids of pointers are UNSIGNED.
set<DefOrUse, LessDefUse>::iterator DefIter, UseIter;
op_t DefOp, UseOp;
int SSANum;
int DefHashValue, UseHashValue;
SMPOperandType DefType, UseType;
unsigned short DefSignMiscInfo = FG_MASK_UNSIGNED, UseSignMiscInfo = FG_MASK_UNSIGNED;
bool GlobalName;
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
DefOp = DefIter->GetOp();
if (MDIsGeneralPurposeReg(DefOp)) {
DefType = DefIter->GetType();
if (IsDataPtr(DefType) || (CODEPTR == DefType)) {
GlobalName = this->BasicBlock->GetFunc()->IsGlobalName(DefOp);
SSANum = DefIter->GetSSANum();
DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum);
if (GlobalName) {
this->BasicBlock->GetFunc()->UpdateDefSignMiscInfo(DefHashValue, DefSignMiscInfo);
}
else {
this->BasicBlock->UpdateDefSignMiscInfo(DefHashValue, DefSignMiscInfo);
}
}
}
}
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseOp = UseIter->GetOp();
if (MDIsGeneralPurposeReg(UseOp)) {
UseType = UseIter->GetType();
if (IsDataPtr(UseType) || (CODEPTR == UseType)) {
GlobalName = this->BasicBlock->GetFunc()->IsGlobalName(UseOp);
SSANum = UseIter->GetSSANum();
UseHashValue = HashGlobalNameAndSSA(UseOp, SSANum);
if (GlobalName) {
this->BasicBlock->GetFunc()->UpdateUseSignMiscInfo(UseHashValue, UseSignMiscInfo);
}
else {
this->BasicBlock->UpdateUseSignMiscInfo(UseHashValue, UseSignMiscInfo);
}
}
}
}
return;
} // end of SMPInstr::InferSignednessFromSMPTypes()
// Helper to set width info for a UseOp from an RTL
void SMPInstr::SetRTLUseOpRegWidthInfo(op_t UseOp) {
unsigned short WidthMask;
set<DefOrUse, LessDefUse>::iterator UseIter;
unsigned int UseHashValue;
if (MDIsGeneralPurposeReg(UseOp)) {
WidthMask = ComputeOperandBitWidthMask(UseOp, 0);
CanonicalizeOpnd(UseOp);
UseIter = this->FindUse(UseOp);
assert(UseIter != this->GetLastUse());
UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(UseOp)) {
this->BasicBlock->UpdateUseWidthTypeInfo(UseHashValue, WidthMask);
}
else {
this->BasicBlock->GetFunc()->UpdateUseWidthTypeInfo(UseHashValue, WidthMask);
}
}
return;
} // end of SMPInstr::SetRTLUseOpRegWidthInfo()
// Walk the RTL and update the register USE operands' width info.
void SMPInstr::MDSetRTLRegWidthInfo(SMPRegTransfer *CurrRT) {
op_t UseOp;
UseOp = CurrRT->GetLeftOperand();
this->SetRTLUseOpRegWidthInfo(UseOp);
if (CurrRT->HasRightSubTree()) {
this->MDSetRTLRegWidthInfo(CurrRT->GetRightTree());
}
else {
UseOp = CurrRT->GetRightOperand();
this->SetRTLUseOpRegWidthInfo(UseOp);
}
return;
} // end of SMPInstr::MDSetRTLRegWidthInfo()
// Do we not consider truncation on this type of instruction to be an error?
bool SMPInstr::IsBenignTruncation(int &IdiomCode) {
bool benign = false;
unsigned short SignMask;
op_t UseOp, SearchOp;
if (3 == this->GetOptType()) { // Move instruction
bool ExtendedLoad = this->MDIsSignedLoad(SignMask);
if (ExtendedLoad && (SignMask & FG_MASK_UNSIGNED)) {
// We have a zero-extended load. Compilers zero-extend both
// signed (unfortunately) and unsigned sub-regs when they know
// from the source language types that only the lower bits matter,
// e.g. when a char has been stored in the lower bits and regardless
// of whether that char was sign-extended or zero-extended previously,
// only the char itself is useful info. Otherwise, the compiler could
// move the whole register, e.g. instead of edi := zero-extend(cx), the
// compiler could have generated edi := ecx. The zero-extension loads
// are therefore not good candidates for truncation checks, as they lead
// to lots of false positives.
benign = true;
IdiomCode = 5;
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++BenignTruncationCount;
#endif
}
else { // Move, and not extended load, which was handled above.
// Next case: A move instruction whose USE falsely appears to be a truncation,
// but in fact the apparently unused portion of the register is used later, e.g.:
// mov [ebp-12],ax ; looks like EAX is being truncated to AX and stored
// shr eax,16 ; gets upper 16 bits into lower 16 bits
// mov [ebp-14],ax ; store what used to be the upper 16 bits of EAX
// The first instruction will trigger a CHECK TRUNCATION annotation that
// causes false positives. We need to analyze the context of the instruction
// to see that the whole register EAX was used, so no truncation occurred.
// The context analysis in the basic block will mark the second move as
// a "truncation" that should be ignored, so we check the flag here to short
// circuit redundant analysis.
UseOp = this->GetMoveSource();
assert(o_void != UseOp.type);
SearchOp = UseOp;
if (o_reg == UseOp.type) {
CanonicalizeOpnd(SearchOp);
}
set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(SearchOp);
assert(UseIter != this->GetLastUse());
if (UseIter->DoesNotTruncate()) {
benign = true;
IdiomCode = 2;
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++SuppressTruncationRegPiecesAllUsed;
#endif
}
else {
set<DefOrUse, LessDefUse>::iterator DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
int DefSSANum = DefIter->GetSSANum();
benign = this->GetBlock()->IsBenignTruncationDEF(DefIter->GetOp(), DefSSANum, this->GetAddr(), IdiomCode);
if (!benign) {
// Third case: value gets zero-extended, AND/OR instructions manipulate it, then
// the lower bits get written. No adds, subtracts, etc. in the chain of operations.
// This is just the typical unwillingness of compiler to do less than 32-bit arithmetic,
// not an actual truncation.
int UseSSANum = UseIter->GetSSANum();
// Prepare for possible recursive traversals through the function blocks.
this->GetBlock()->GetFunc()->ResetProcessedBlocks();
if (this->IsOpSourceZeroExtendedMoveShiftRightOrConditionCode(SearchOp, UseSSANum, true)) {
IdiomCode = 14;
benign = true;
}
}
}
}
}
return benign;
} // end of SMPInstr::IsBenignTruncation()
// Do we not consider overflow or underflow on this type of instruction to be an error?
bool SMPInstr::IsBenignOverflow(int &IdiomCode) {
bool benign = false;
set<DefOrUse, LessDefUse>::iterator DefIter, UseIter;
SMPOperandType DefType, UseType;
int DefSSANum;
ea_t DefAddr;
op_t DefOp;
if (this->MDIsDefiniteBenignUnderflowOpcode(IdiomCode)) {
// No further analysis at block or function scope is needed.
benign = true;
}
else if (this->MDIsMaybeBenignUnderflowOpcode()) { // might have the subtract instruction in tricky sequence
// We are looking to suppress overflow and underflow warnings on the following
// code sequence: PTR1-PTR2+1 gets a loop invariant code motion optimization
// that pulls temp := 1-PTR2 out of the loop, and leaves temp2 := PTR1+temp
// inside the loop. The hoisted subtraction could underflow, and the addition
// that is not hoisted could overflow. The net effect of these two instructions
// is benign, however, so we want to suppress underflow and overflow checks on
// both of them, but only if we can match the pair of instructions.
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
if (DefIter->DoesNotOverflow()) {
benign = true; // short circuit; already analyzed
}
else {
DefType = DefIter->GetType();
if (IsEqType(DefType, NEGATEDPTR)) { // We have candidate subtract instruction
DefOp = DefIter->GetOp();
DefSSANum = DefIter->GetSSANum();
DefAddr = this->GetAddr();
benign = this->GetBlock()->GetFunc()->IsBenignUnderflowDEF(DefOp, DefSSANum, DefAddr, IdiomCode);
if (!benign) {
// If we did not find the complicated sequence, we still don't want to have a false positive
// on any instruction that is producing a NEGATEDPTR.
benign = true;
IdiomCode = 13;
}
}
}
}
else if (this->MDIsMaybeBenignOverflowOpcode()) { // might have the add instruction in tricky sequence
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
if (DefIter->DoesNotOverflow()) {
benign = true; // short circuit; already analyzed
IdiomCode = 6;
}
else { // Bad luck to encounter addition first. See what types tell us.
if (this->HasNegatedPtrUSE()) { // We have candidate for addition.
DefType = DefIter->GetType();
if (IsEqType(DefType, PTROFFSET)) {
// Current instruction is definitely the addition instruction of some
// benignly underflowing and overflowing pair of instructions. It does
// not really matter that we have not found the subtraction yet; we will
// get to it eventually. We should suppress overflow checks on this inst.
DefOp = DefIter->GetOp();
this->Defs.SetNoOverflow(DefOp, true);
benign = true;
IdiomCode = 6;
}
else {
// A bit of a quandary. Ideally, we would have successful SMP type inference
// and always have PTROFFSET := POINTER + NEGATEDPTR. However, sometimes we
// might have ?? := ?? + NEGATEDPTR. The instruction could be of the type
// NEGATEDPTR := NUMERIC + NEGATEDPTR, so we cannot just assume here that
// we have detected the benign case. However, these instructions are likely
// to cause false positives and unlikely to be a source of false negatives,
// so we will err on the side of false positive suppression based on experience
// (even though we usually do the opposite).
benign = true;
IdiomCode = 13;
}
}
}
}
if (!benign) {
// Any overflow or underflow producing a PTROFFSET type should be suppressed.
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
DefType = DefIter->GetType();
if (IsEqType(DefType, PTROFFSET)) {
benign = true;
IdiomCode = 19;
}
}
}
if (!benign) {
// Any overflow or underflow using a PTROFFSET type should be suppressed.
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseType = UseIter->GetType();
if (IsEqType(UseType, PTROFFSET)) {
benign = true;
IdiomCode = 19;
}
}
}
return benign;
} // end of SMPInstr::IsBenignOverflow()
// Do we detect a situation in which it is safe to check for signedness errors on
// the stack write (return false), or not (return true to be safe).
bool SMPInstr::SkipSignednessCheckOnStackWrite(int DefSSANum) {
bool SkipCheck = (!(this->IsUnsignedArg()));
op_t StackDefOp = this->DEFMemOp;
size_t DefBitWidth = 8 * GetOpDataSize(StackDefOp);
if (DefBitWidth < MD_NORMAL_MACHINE_BITWIDTH) {
// If we are not dealing with a shortened bit width, then
// the next load from the stack will not be sign-extended
// or zero-extended.
if (this->GetBlock()->IsStackOpNextUsedWithSignedness(StackDefOp, this->GetAddr(), DefSSANum)) {
SkipCheck = false;
}
}
return SkipCheck;
} // end of SMPInstr::SkipSignednessCheckOnStackWrite()
// Does inst pass an outgoing argument? If so, pass back argument position #, starting at zero for [esp], 1 for [esp+4], etc.
bool SMPInstr::MDIsArgumentPass(size_t &ArgumentNumber) {
bool OutArgPass = false;
// Current model is writing outargs to stack. For other compiler targets in the
// future, we would also include pushes onto the stack.
if (this->HasDestMemoryOperand() && (this->GetOptType() == 3)) { // move to memory
if (this->GetBlock()->GetFunc()->OutArgsRegionComputed()) {
op_t DefOp = this->DEFMemOp;
OutArgPass = this->GetBlock()->GetFunc()->IsInOutgoingArgsRegion(DefOp);
if (OutArgPass) {
op_t UnnormalizedDefOp = DefOp;
this->MDGetUnnormalizedOp(UnnormalizedDefOp);
size_t StandardByteSize = (MD_NORMAL_MACHINE_BITWIDTH / 8);
ArgumentNumber = (UnnormalizedDefOp.addr / StandardByteSize);
}
}
}
return OutArgPass;
}
// Does UseOp arithmetically affect the value of the NonFlagsDef for this inst?
bool SMPInstr::OperandTransfersValueToDef(op_t UseOp) {
bool FoundTransfer = false;
for (size_t index = 0; index < this->RTL.GetCount(); ++index) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(index);
if (CurrRT->OperandTransfersValueToDef(UseOp)) {
FoundTransfer = true;
break;
}
}
return FoundTransfer;
} // end of SMPInstr::OperandTransfersValueToDef()
// Trace UseOp through register moves back to its stack location or immediate value source.
// Return true if we are passing an immediate or stack location back in UltSource.
bool SMPInstr::TraceUltimateMoveSource(op_t UseOp, int UseSSANum, op_t &UltSource, bool &FPRelative) {
// If we hit an immediate value or a stack location, we are done.
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
op_t NewUseOp; // next UseOp up the move chain
op_t DefOp, ImmOp;
int NewUseSSANum;
set<DefOrUse,LessDefUse>::iterator UseIter;
bool LocalName;
ea_t DefAddr;
SMPInstr *DefInst;
UltSource = InitOp;
bool StackOp = MDIsStackAccessOpnd(UseOp, UseFP);
bool RegisterOp = (UseOp.type == o_reg);
if (this->GetOptType() == 3) { // move instruction
if (UseOp.type == o_imm) {
UltSource = UseOp;
return true;
}
else if ((!RegisterOp) && (!StackOp)) {
// We only trace the move chain through registers or stack locations to an ultimate
// load-effective-address of a stack location, or a move of an immediate value.
return false;
}
}
else if (!this->MDIsLoadEffectiveAddressInstr()) {
return false;
}
else { // Load effective address instruction.
// If it is a stack location being loaded, trace succeeded, else it failed.
if (StackOp) {
UltSource = UseOp;
FPRelative = this->HasFPNormalizedToSP();
return true;
}
else {
return false;
}
}
// If we reach this point, we have a move instruction but did not return true or false above.
// Recursion case. Going back up the move chain has just produced a register or
// a stack location, and we still need to find the stack address or immediate value
// that was stored in the register or stack location. The stack location could hold
// a pointer to a stack object, produced by an earlier LEA instruction, or it
// could hold an immediate value (e.g. constant size argument passed to memset() or
// similar function).
LocalName = this->GetBlock()->IsLocalName(UseOp);
DefAddr = this->GetBlock()->GetDefAddrFromUseAddr(UseOp, this->GetAddr(), UseSSANum, LocalName);
if ((BADADDR == DefAddr) || (DefAddr < (this->GetBlock()->GetFunc()->GetStartAddr() - 1))) {
// Def was not found, or was found in Phi function (DefAddr was block number, not instruction addr).
return false;
}
if (DefAddr < (this->GetBlock()->GetFirstAddr())) {
// If DefAddr is 1 byte less than the first addr in the block, then
// it is a pseudo-def in the global DU chains, signifying that the
// value was LiveIn and the true DEF is in another block. We could
// handle this in the future, but right now we will only deal with
// the simpler case in which the move source can be traced
// within the basic block.
return false;
}
// Everything is OK so far; time to recurse up the move chain.
DefInst = this->GetBlock()->GetFunc()->GetInstFromAddr(DefAddr);
if (DefInst->GetOptType() == 3) {
NewUseOp = DefInst->GetMoveSource();
}
else if (DefInst->MDIsLoadEffectiveAddressInstr()) {
NewUseOp = DefInst->GetLeaMemUseOp();
if (MDIsStackAccessOpnd(NewUseOp, UseFP)) {
UltSource = NewUseOp;
FPRelative = DefInst->HasFPNormalizedToSP();
return true;
}
else {
return false;
}
}
// We don't have a move instruction or a load effective address instruction, which
// can be used to move a stack address into a register. We don't try to trace through
// arithmetic except for two easy cases.
// Case 1: A register is cleared. Same as assigning immediate value zero to the reg.
else if (DefInst->IsRegClearIdiom()) {
UltSource.type = o_imm;
UltSource.value = 0; // why would we memset a zero byte region?
return true;
}
// Easy arithmetic Case 2: we have reg += ImmediateValue, and reg was DEFed by reg := LEA(StackLoc).
else if (DefInst->MDIsAddImmediateToReg(DefOp, ImmOp)) {
SMPInstr *NewDefInst;
UseIter = DefInst->FindUse(DefOp);
assert(UseIter != DefInst->GetLastUse());
NewUseSSANum = UseIter->GetSSANum();
LocalName = DefInst->GetBlock()->IsLocalName(DefOp);
DefAddr = DefInst->GetBlock()->GetDefAddrFromUseAddr(DefOp, DefInst->GetAddr(), NewUseSSANum, LocalName);
if ((BADADDR == DefAddr) || (DefAddr < (DefInst->GetBlock()->GetFunc()->GetStartAddr() - 1))) {
// Def was not found, or was found in Phi function (DefAddr was block number, not instruction addr).
return false;
}
NewDefInst = DefInst->GetBlock()->GetFunc()->GetInstFromAddr(DefAddr);
if (NewDefInst->MDIsLoadEffectiveAddressInstr()) {
NewUseOp = NewDefInst->GetLeaMemUseOp();
if (MDIsStackAccessOpnd(NewUseOp, UseFP)) {
// We have the code sequence we were searching for when we first saw the
// addition of an immediate value to a register, e.g.:
// lea ebx,[ebp-2000]
// add ebx,1000
//
// We are essentially making this sequence into a single instruction:
// lea ebx,[ebp-1000]
// by adding the immediate value to the address offset. With a stack that grows
// downward, it does not matter if we add 1000 to [esp+500] to produce [esp+1500],
// or we add 1000 to [ebp-2000] to make [ebp-1000]. Either way, we are simulating the
// addition of 1000 as we move up in the stack frame.
NewUseOp.addr += ImmOp.value; // perform the address arithmetic addition
UltSource = NewUseOp;
FPRelative = NewDefInst->HasFPNormalizedToSP();
return true;
}
else {
return false;
}
}
else {
return false;
}
}
else {
// Not the kind of instruction we need; cut short the recursion.
return false;
}
// NewUseOp is the move source operand that we seek.
UseIter = DefInst->FindUse(NewUseOp);
assert(UseIter != DefInst->GetLastUse());
NewUseSSANum = UseIter->GetSSANum(); // unused for immediates, used for regs and stack
// Recurse
return DefInst->TraceUltimateMoveSource(NewUseOp, NewUseSSANum, UltSource, FPRelative);
} // end of SMPInstr::TraceUltimateMoveSource()
// inst has no code xrefs?
bool SMPInstr::HasNoCodeXrefs(void) {
SMP_xref_t CurrXrefs;
bool FoundCodeXref = false;
for (bool ok = CurrXrefs.SMP_first_to(this->GetAddr(), XREF_ALL); ok; ok = CurrXrefs.SMP_next_to()) {
ea_t FromAddr = CurrXrefs.GetFrom();
if ((FromAddr != 0) && (CurrXrefs.GetIscode())) {
FoundCodeXref = true;
break;
}
}
return (!FoundCodeXref);
} // end of SMPInstr::HasNoCodeXrefs()
// Infer DEF, USE, and RTL SMPoperator types within the instruction based on the type
// of operator, the type category of the instruction, and the previously known types
// of the operands.
bool SMPInstr::InferTypes(void) {
bool changed = false; // return value
int SSANum;
int TypeCategory = SMPTypeCategory[this->SMPcmd.itype];
set<DefOrUse, LessDefUse>::iterator CurrDef;
set<DefOrUse, LessDefUse>::iterator CurrUse;
op_t DefOp = InitOp, UseOp = InitOp;
bool DebugFlag = false;
bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
bool SafeFunc = this->BasicBlock->GetFunc()->IsSafe();
bool IsMemOp;
#if SMP_VERBOSE_DEBUG_INFER_TYPES
DebugFlag |= (0 == strcmp("InputMove", this->BasicBlock->GetFunc()->GetFuncName()));
#endif
if (DebugFlag) {
SMP_msg("opcode: %d TypeCategory: %d\n", this->SMPcmd.itype, TypeCategory);
}
// If we are already finished with all types, return false.
if (this->IsTypeInferenceComplete())
return false;
if (this->AllDEFsTyped() && this->AllUSEsTyped()) {
this->SetTypeInferenceComplete();
return false;
}
if (this->HasDestMemoryOperand()) {
changed |= this->MDFindPointerUse(this->MDGetMemDefOp(), UseFP);
}
if (this->HasSourceMemoryOperand()) {
changed |= this->MDFindPointerUse(this->MDGetMemUseOp(), UseFP);
}
// The control flow instructions can be handled simply based on their type
// and do not need an RTL walk.
SMPitype DFAType = this->GetDataFlowType();
bool CallInst = ((DFAType == CALL) || (DFAType == INDIR_CALL) || this->IsTailCall());
ushort IndirCallReg = R_none;
if (DebugFlag) {
SMP_msg("DFAType: %d CategoryInferenceComplete: %d\n",
DFAType, this->IsCategoryInferenceComplete());
}
if (DFAType == INDIR_CALL) {
op_t TargetOp = this->SMPcmd.Operands[0];
if (TargetOp.type == o_reg)
IndirCallReg = TargetOp.reg;
}
if (((DFAType >= JUMP) && (DFAType <= INDIR_CALL)) || this->IsTailCall()) {
// All USEs are either the flags (NUMERIC) or the target address (CODEPTR).
// The exceptions are the USE list for interrupt calls, which includes
// the caller-saved regs, and indirect calls through a memory
// operand, such as call [ebx+esi+20h], where the memory operand
// is a CODEPTR but the addressing registers are a BaseReg and
// IndexReg as in any other memory addressing, and the saved
// regs on any call.
CurrUse = this->GetFirstUse();
while (CurrUse != this->GetLastUse()) {
UseOp = CurrUse->GetOp();
if (UseOp.is_reg(X86_FLAGS_REG))
CurrUse = this->SetUseType(UseOp, NUMERIC);
else if ((CurrUse->GetType() != CODEPTR)
&& (!(this->MDIsInterruptCall() && (o_reg == UseOp.type)))
&& (!(CallInst && (o_reg == UseOp.type)))
&& (!(this->HasSourceMemoryOperand()
&& (INDIR_CALL == this->GetDataFlowType())
&& (o_reg == UseOp.type)))) {
CurrUse = this->SetUseType(UseOp, CODEPTR);
if (CallInst && (DFAType != INDIR_CALL)) {
// If the call is to malloc(), then the DEF of the return
// register is of type HEAPPTR.
// ****!!!!**** Could have INDIR_CALL resolved to malloc.
changed |= this->MDFindMallocCall(UseOp);
}
}
else if ((CurrUse->GetType() != CODEPTR) && CallInst
&& UseOp.is_reg(IndirCallReg)) {
CurrUse = this->SetUseType(UseOp, CODEPTR);
}
++CurrUse;
}
this->SetTypeInferenceComplete();
return true;
}
// First, see if we can infer something about DEFs and USEs just from the
// type category of the instruction.
if (!this->IsCategoryInferenceComplete()) {
bool MemPropagate = false;
switch (TypeCategory) {
case 0: // no inference possible just from type category
case 1: // no inference possible just from type category
case 3: // MOV instructions; inference will come from source to dest in RTL walk.
case 5: // binary arithmetic; inference will come in RTL walk.
case 10: // binary arithmetic; inference will come in RTL walk.
case 11: // push and pop instructions; inference will come in RTL walk.
case 12: // exchange instructions; inference will come in RTL walk.
this->SetCategoryInferenceComplete();
break;
case 2: // Result type is always NUMERIC.
case 7: // Result type is always NUMERIC.
case 8: // Result type is always NUMERIC.
case 9: // Result type is always NUMERIC.
case 13: // Result type is always NUMERIC.
case 14: // Result type is always NUMERIC.
case 15: // Result type is always NUMERIC.
CurrDef = this->GetFirstDef();
while (CurrDef != this->GetLastDef()) {
if (!IsEqType(NUMERIC, CurrDef->GetType())) {
DefOp = CurrDef->GetOp();
SSANum = CurrDef->GetSSANum();
CurrDef = this->SetDefType(DefOp, NUMERIC);
changed = true;
// Be conservative and only propagate register DEFs and SAFE stack locs. We
// can improve this in the future. **!!**
bool IsMemOp = (o_reg != DefOp.type);
bool MemPropagate = MDIsStackAccessOpnd(DefOp, UseFP);
#if SMP_PROPAGATE_MEM_TYPES
;
#else
// Be conservative and only propagate register DEFs and SAFE stack locs.
// We can improve this in the future. **!!**
MemPropagate = MemPropagate && SafeFunc;
#endif
if ((o_reg == DefOp.type) || MemPropagate) {
if (this->BasicBlock->IsLocalName(DefOp)) {
(void) this->BasicBlock->PropagateLocalDefType(DefOp, NUMERIC,
this->GetAddr(), SSANum, IsMemOp);
}
else { // global name
this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false
(void) this->BasicBlock->PropagateGlobalDefType(DefOp, NUMERIC,
SSANum, IsMemOp);
}
}
}
++CurrDef;
}
this->SetCategoryInferenceComplete();
break;
case 4: // Unary INC, DEC, etc.: dest=source, so type remains the same
assert(1 == this->RTL.GetCount());
assert(this->RTL.GetRT(0)->HasRightSubTree());
UseOp = this->RTL.GetRT(0)->GetLeftOperand(); // USE == DEF
CurrUse = this->Uses.FindRef(UseOp);
assert(CurrUse != this->GetLastUse());
if (UNINIT != CurrUse->GetType()) {
// Only one USE, and it has a type assigned, so assign that type
// to the DEF.
CurrDef = this->GetFirstDef();
while (CurrDef != this->GetLastDef()) {
// Two DEFs: EFLAGS is NUMERIC, dest==source
DefOp = CurrDef->GetOp();
SSANum = CurrDef->GetSSANum();
if (DefOp.is_reg(X86_FLAGS_REG)) {
; // SetImmedTypes already made it NUMERIC
}
else {
CurrDef = this->SetDefType(DefOp, CurrUse->GetType());
// Be conservative and only propagate register DEFs and SAFE stack locs. We
// can improve this in the future. **!!**
bool IsMemOp = (o_reg != DefOp.type);
MemPropagate = MDIsStackAccessOpnd(DefOp, UseFP);
#if SMP_PROPAGATE_MEM_TYPES
;
#else
// Be conservative and only propagate register DEFs and SAFE stack locs.
// We can improve this in the future. **!!**
MemPropagate = MemPropagate && SafeFunc;
#endif
if ((o_reg == DefOp.type) || MemPropagate) {
if (this->BasicBlock->IsLocalName(DefOp)) {
(void) this->BasicBlock->PropagateLocalDefType(DefOp, CurrUse->GetType(),
this->GetAddr(), SSANum, IsMemOp);
}
else { // global name
this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false
(void) this->BasicBlock->PropagateGlobalDefType(DefOp, CurrUse->GetType(),
SSANum, IsMemOp);
}
}
}
++CurrDef;
}
this->SetCategoryInferenceComplete();
changed = true;
this->SetTypeInferenceComplete();
}
break;
case 6: // Result is always POINTER
DefOp = this->GetFirstDef()->GetOp();
SSANum = this->GetFirstDef()->GetSSANum();
CurrDef = this->SetDefType(DefOp, POINTER);
this->SetCategoryInferenceComplete();
changed = true;
// Be conservative and only propagate register DEFs and SAFE stack locs. We
// can improve this in the future. **!!**
IsMemOp = (o_reg != DefOp.type);
MemPropagate = MDIsStackAccessOpnd(DefOp, UseFP);
#if SMP_PROPAGATE_MEM_TYPES
;
#else
// Be conservative and only propagate register DEFs and SAFE stack locs.
// We can improve this in the future. **!!**
MemPropagate = MemPropagate && SafeFunc;
#endif
if ((o_reg == DefOp.type) || MemPropagate) {
if (this->BasicBlock->IsLocalName(DefOp)) {
(void) this->BasicBlock->PropagateLocalDefType(DefOp, POINTER,
this->GetAddr(), SSANum, IsMemOp);
}
else { // global name
this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false
(void) this->BasicBlock->PropagateGlobalDefType(DefOp, POINTER,
SSANum, IsMemOp);
}
}
break;
default:
SMP_msg("ERROR: Unknown type category for %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
this->SetCategoryInferenceComplete();
break;
} // end switch on TypeCategory
} // end if (!CategoryInference)
// Walk the RTL and infer types based on operators and operands.
if (DebugFlag) {
SMP_msg("RTcount: %zu\n", this->RTL.GetCount());
}
for (size_t index = 0; index < this->RTL.GetCount(); ++index) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(index);
if (SMP_NULL_OPERATOR == CurrRT->GetOperator()) // nothing to infer
continue;
if (!(CurrRT->IsTypeInferenceComplete())) {
changed |= this->InferOperatorType(CurrRT);
}
if (DebugFlag) {
SMP_msg("returned from InferOperatorType\n");
}
} // end for all RTs in the RTL
return changed;
} // end of SMPInstr::InferTypes()
// Infer the type of an operator within an RT based on the types of its operands and
// based on the operator itself. Recurse down the tree if necessary.
// Return true if the operator type of the RT is updated.
bool SMPInstr::InferOperatorType(SMPRegTransfer *CurrRT) {
bool updated = false;
bool LeftNumeric, RightNumeric, OperNumeric;
bool LeftPointer, RightPointer, OperPointer;
bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
bool SafeFunc = this->BasicBlock->GetFunc()->IsSafe();
set<DefOrUse, LessDefUse>::iterator CurrDef;
set<DefOrUse, LessDefUse>::iterator CurrUse;
set<DefOrUse, LessDefUse>::iterator LeftUse;
set<DefOrUse, LessDefUse>::iterator RightUse;
SMPOperandType LeftType = UNINIT;
SMPOperandType RightType = UNINIT;
SMPOperandType OperType = UNINIT;
op_t UseOp = InitOp, DefOp = InitOp, LeftOp = InitOp, RightOp = InitOp;
SMPoperator CurrOp = CurrRT->GetOperator();
bool TypeInferenceFinished = false;
#if SMP_VERBOSE_DEBUG_INFER_TYPES
bool DebugFlag = false;
#if 1
DebugFlag |= (0 == strcmp("InputMove", this->BasicBlock->GetFunc()->GetFuncName()));
#endif
DebugFlag = DebugFlag || ((this->address == 0x806453b) || (this->address == 0x806453e));
#endif
#if SMP_VERBOSE_DEBUG_INFER_TYPES
if (DebugFlag) {
SMP_msg("Entered InferOperatorType for CurrOp: %d at %x\n", CurrOp, this->GetAddr());
}
#endif
if (CurrRT->IsTypeInferenceComplete()) {
return updated;
}
switch (CurrOp) {
case SMP_NULL_OPERATOR:
case SMP_SIGNAL: // signal or raise exception
TypeInferenceFinished = true;
break;
case SMP_CALL: // CALL instruction
if (UNINIT == CurrRT->GetOperatorType()) {
CurrRT->SetOperatorType(CODEPTR, this);
updated = true;
UseOp = CurrRT->GetRightOperand();
CurrUse = this->Uses.FindRef(UseOp);
assert(CurrUse != this->GetLastUse());
if (UNINIT == CurrUse->GetType()) {
CurrUse = this->SetUseType(UseOp, CODEPTR);
}
else if (CODEPTR != CurrUse->GetType()) {
SMP_msg("WARNING: call target is type %d, setting to CODEPTR at %x in %s\n",
CurrUse->GetType(), this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
CurrUse = this->SetUseType(UseOp, CODEPTR);
}
}
TypeInferenceFinished = true;
break;
case SMP_INPUT: // input from port
if (UNINIT == CurrRT->GetOperatorType()) {
CurrRT->SetOperatorType(NUMERIC, this);
updated = true;
}
break;
case SMP_OUTPUT: // output to port
if (UNINIT == CurrRT->GetOperatorType()) {
CurrRT->SetOperatorType(NUMERIC, this);
updated = true;
}
break;
case SMP_SIGN_EXTEND:
case SMP_ZERO_EXTEND:
// Should we infer that all operands are NUMERIC? !!!???!!!!
break;
case SMP_ADDRESS_OF: // take effective address
if (UNINIT == CurrRT->GetOperatorType()) {
CurrRT->SetOperatorType(POINTER, this);
// Left operand is having its address taken, but we cannot infer what its
// type is.
updated = true;
}
break;
case SMP_U_LEFT_SHIFT: // unsigned left shift
case SMP_S_LEFT_SHIFT: // signed left shift
case SMP_U_RIGHT_SHIFT: // unsigned right shift
case SMP_S_RIGHT_SHIFT: // signed right shift
case SMP_ROTATE_LEFT:
case SMP_ROTATE_LEFT_CARRY: // rotate left through carry
case SMP_ROTATE_RIGHT:
case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry
case SMP_U_MULTIPLY:
case SMP_S_MULTIPLY:
case SMP_U_DIVIDE:
case SMP_S_DIVIDE:
case SMP_U_REMAINDER:
case SMP_BITWISE_XOR:
case SMP_BITWISE_AND_NOT:
case SMP_S_COMPARE: // signed compare (subtraction-based)
case SMP_U_COMPARE: // unsigned compare (AND-based)
case SMP_GENERAL_COMPARE:
case SMP_LESS_THAN: // boolean test operators
case SMP_GREATER_THAN:
case SMP_LESS_EQUAL:
case SMP_GREATER_EQUAL:
case SMP_EQUAL:
case SMP_NOT_EQUAL:
case SMP_LOGICAL_AND:
case SMP_LOGICAL_OR:
case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC
case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
case SMP_REVERSE_SHIFT_U: // all the same to our type system; all NUMERIC
case SMP_SHUFFLE: // all the same to our type system; all NUMERIC
case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's
case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's
case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's
case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's
case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's
case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's
case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision
case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision
case SMP_AVERAGE_U: // Average of unsigned operands
case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate)
case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements
case SMP_MAX_S: // dest := signed_max(dest, src)
case SMP_MAX_U: // dest := unsigned_max(dest, src)
case SMP_MIN_S: // dest := signed_min(dest, src)
case SMP_MIN_U: // dest := unsigned_min(dest, src)
case SMP_ABSOLUTE_VALUE: // take absolute value
case SMP_CONVERT_INT_TO_FP: // convert integer to floating point
case SMP_CONVERT_FP_TO_INT: // convert floating point to integer
case SMP_CREATE_MASK: // Create AND-mask from operand and byte/word/dword position # in immediate
case SMP_INTERLEAVE: // interleave fields from two packed operands; NUMERIC
case SMP_CONCATENATE: // all the same to our type system; all NUMERIC
case SMP_ENCRYPTION_OPERATION: // encryption or decryption bit manipulation operation
case SMP_EXTRACT_ZERO_EXTEND: // Extract sub-reg and zero-extend to reg length
if (UNINIT == CurrRT->GetOperatorType()) {
CurrRT->SetOperatorType(NUMERIC, this);
updated = true;
}
// Left operand should be NUMERIC if it exists.
UseOp = CurrRT->GetLeftOperand();
if (UseOp.type != o_void) {
CurrUse = this->Uses.FindRef(UseOp);
if (CurrUse == this->GetLastUse()) {
SMP_msg("SERIOUS WARNING: Adding missing USE of ");
PrintOperand(UseOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
this->Uses.SetRef(UseOp, NUMERIC, -1);
updated = true;
}
else if (UNINIT == CurrUse->GetType()) {
CurrUse = this->SetUseType(UseOp, NUMERIC);
updated = true;
}
}
// Right operand should be NUMERIC if it exists.
if (CurrRT->HasRightSubTree()) {
// Recurse into subtree
#if SMP_AGGRESSIVE_TYPE_INFERENCE
if (UNINIT == CurrRT->GetRightTree()->GetOperatorType()) {
CurrRT->GetRightTree()->SetOperatorType(NUMERIC, this);
}
#endif
updated |= this->InferOperatorType(CurrRT->GetRightTree());
}
else {
UseOp = CurrRT->GetRightOperand();
if (UseOp.type != o_void) {
CurrUse = this->Uses.FindRef(UseOp);
if (CurrUse == this->GetLastUse()) {
SMP_msg("SERIOUS WARNING: Adding missing USE of ");
PrintOperand(UseOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
this->Uses.SetRef(UseOp, NUMERIC, -1);
updated = true;
}
else if (UNINIT == CurrUse->GetType()) {
CurrUse = this->SetUseType(UseOp, NUMERIC);
updated = true;
}
}
}
break;
case SMP_BITWISE_NOT: // unary operator
case SMP_NEGATE: // unary negation
UseOp = CurrRT->GetLeftOperand();
assert(o_void != UseOp.type);
CurrUse = this->Uses.FindRef(UseOp);
if (CurrUse == this->GetLastUse()) {
SMP_msg("SERIOUS WARNING: Adding missing USE of ");
PrintOperand(UseOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
updated = true;
}
else {
OperType = CurrRT->GetOperatorType();
LeftType = CurrUse->GetType();
// Only tricky cases are the negation of a POINTER or PTROFFSET.
// Negation of PTROFFSET could be inefficient code that computed
// PTR1 - PTR2 and later corrected it to PTR2 - PTR1 by negation.
// The type remains PTROFFSET. Negating a POINTER could be an unusual
// case similar to subtracting a POINTER from a NUMERIC. See comments
// in the SMP_ADD case below, and also the SMP_SUBTRACT case.
if (LeftType == PTROFFSET) {
// Override any prior operator type, in case PTROFFSET was inferred late
// in our analysis and the operator was set to NUMERIC.
CurrRT->SetOperatorType(PTROFFSET, this);
updated = true;
if (CurrOp == SMP_BITWISE_NOT)
SMP_msg("INFO: NOT of PTROFFSET at %x %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
else if (IsDataPtr(LeftType)) {
// Override any prior operator type, in case POINTER was inferred late
// in our analysis and the operator was set to NUMERIC.
CurrRT->SetOperatorType(NEGATEDPTR, this);
SMP_msg("INFO: Pointer negated at %x %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
updated = true;
}
else if (OperType == UNINIT) {
// Default to NUMERIC for most negations.
CurrRT->SetOperatorType(NUMERIC, this);
// But, leave left operand type alone, in case an UNINIT operand
// might be determined later to be PTROFFSET or NEGATEDPTR.
// Leaving it alone causes us not to set TypeInferenceFinished to true
// at the end of this function in the UNINIT case.
updated = true;
}
}
break;
case SMP_INCREMENT:
case SMP_DECREMENT:
// The type of the left operand is propagated to the operator, or vice
// versa, whichever receives a type first.
assert(!CurrRT->HasRightSubTree());
UseOp = CurrRT->GetLeftOperand();
assert(o_void != UseOp.type);
CurrUse = this->Uses.FindRef(UseOp);
if (CurrUse == this->GetLastUse()) {
SMP_msg("SERIOUS WARNING: Adding missing USE of ");
PrintOperand(UseOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
this->Uses.SetRef(UseOp);
updated = true;
break;
}
if (UNINIT == CurrRT->GetOperatorType()) {
if (UNINIT != CurrUse->GetType()) {
// Propagate operand type up to the operator.
CurrRT->SetOperatorType(CurrUse->GetType(), this);
updated = true;
}
}
else if (UNINIT == CurrUse->GetType()) {
// Propagate operator type to operand.
CurrUse = this->SetUseType(UseOp, CurrRT->GetOperatorType());
updated = true;
}
break;
case SMP_ADD:
case SMP_ADD_CARRY: // add with carry
case SMP_BITWISE_AND:
case SMP_BITWISE_OR:
// Extract the current types of right and left operands and the operator.
OperType = CurrRT->GetOperatorType();
LeftOp = CurrRT->GetLeftOperand();
CurrUse = this->Uses.FindRef(LeftOp);
assert(CurrUse != this->GetLastUse()); // found it
LeftType = CurrUse->GetType();
if (CurrRT->HasRightSubTree()) {
updated |= this->InferOperatorType(CurrRT->GetRightTree());
RightType = CurrRT->GetRightTree()->GetOperatorType();
}
else {
RightOp = CurrRT->GetRightOperand();
if (o_void == RightOp.type) {
SMP_msg("ERROR: void operand at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
return updated;
}
else {
CurrUse = this->Uses.FindRef(RightOp);
if (CurrUse == this->GetLastUse()) {
SMP_msg("SERIOUS WARNING: Adding missing USE of ");
PrintOperand(RightOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
this->Uses.SetRef(RightOp);
updated = true;
break;
}
else {
RightType = CurrUse->GetType();
}
}
}
// We have to know both operand types to infer the operator, or know the
// operator type and one operand type to infer the other operand type.
if ((UNINIT == OperType)
&& ((UNINIT == LeftType) || (UNINIT == RightType)))
break;
// If both operands are NUMERIC, operator and result are NUMERIC.
// If one operand is NUMERIC and the other is a pointer type,
// then the ADD operator and the result will inherit this second type,
// while AND and OR operators will remain UNINIT (we don't know what
// type "ptr AND 0xfffffff8" has until we see how it is used).
LeftNumeric = IsEqType(NUMERIC, LeftType);
RightNumeric = IsEqType(NUMERIC, RightType);
LeftPointer = IsDataPtr(LeftType);
RightPointer = IsDataPtr(RightType);
if (UNINIT == OperType) {
// Infer operator type from left and right operands.
if (LeftNumeric && RightNumeric) {
CurrRT->SetOperatorType(NUMERIC, this);
updated = true;
break;
}
else if (LeftNumeric || RightNumeric) {
// ADD of NUMERIC to non-NUMERIC preserves non-NUMERIC type.
// AND and OR operations should leave the operator UNINIT for now.
if (LeftNumeric && (UNINIT != RightType)
&& ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp))) {
CurrRT->SetOperatorType(RightType, this);
updated = true;
break;
}
else if (RightNumeric && (UNINIT != LeftType)
&& ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp))) {
CurrRT->SetOperatorType(LeftType, this);
updated = true;
break;
}
}
else if (LeftPointer && RightPointer) {
// Arithmetic on two pointers
if ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp)) {
CurrRT->SetOperatorType(UNKNOWN, this);
updated = true;
}
else { // bitwise AND or OR of two pointers
SMP_msg("WARNING: hash of two pointers at %x in %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
// hash operation? leave operator as UNINIT
}
break;
}
else if ((LeftPointer && IsEqType(RightType, PTROFFSET))
|| (RightPointer && IsEqType(LeftType, PTROFFSET))) {
// Arithmetic on PTR and PTROFFSET
if ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp)) {
// We assume (A-B) is being added to B or vice versa **!!**
CurrRT->SetOperatorType(POINTER, this);
updated = true;
}
else { // bitwise AND or OR of pointer and pointer difference
SMP_msg("WARNING: hash of PTROFFSET and POINTER at %x in %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
// hash operation? leave operator as UNINIT
}
break;
}
else if ((LeftPointer && IsEqType(RightType, NEGATEDPTR))
|| (RightPointer && IsEqType(LeftType, NEGATEDPTR))) {
// Compiler optimizations can take a ptr expression such as:
// PTR1 - PTR2 + 1
// and hoist the loop-invariant subexpression " - PTR2 + 1"
// out of the loop as "1 - PTR2", which produces a NEGATEDPTR type.
// When PTR1 gets its value determined inside the loop, then the
// addition of PTR1 finally happens, producing a PTROFFSET type,
// which is what the whole expression is.
if ((SMP_ADD == CurrOp) || (SMP_ADD_CARRY == CurrOp)) {
CurrRT->SetOperatorType(PTROFFSET, this);
updated = true;
}
else { // bitwise AND or OR of pointer and pointer difference
SMP_msg("WARNING: hash of NEGATEDPTR and POINTER at %x in %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
// hash operation? leave operator as UNINIT
}
break;
}
} // end if UNINIT operator type
else { // operator has type other than UNINIT
// We make add-with-carry and subtract-with-borrow exceptions
// to the type propagation. LeftOp could have POINTER type
// inferred later; these instructions can change the type of
// the register from POINTER to NUMERIC, unlike regular
// add and subtract opcodes.
OperNumeric = IsEqType(NUMERIC, OperType);
OperPointer = IsDataPtr(OperType);
if (OperNumeric) {
if ((UNINIT == LeftType)
&& (SMP_ADD_CARRY != CurrOp)) {
CurrUse = this->SetUseType(LeftOp, CurrRT->GetOperatorType());
updated = true;
assert(CurrUse != this->GetLastUse());
break;
}
if (CurrRT->HasRightSubTree()) {
// Must need to iterate through the right tree again, as the operator
// has been typed.
if (UNINIT == RightType) {
CurrRT->GetRightTree()->SetOperatorType(CurrRT->GetOperatorType(), this);
updated = true;
updated |= this->InferOperatorType(CurrRT->GetRightTree());
}
break;
}
else { // right operand; propagate operator type if needed
if (UNINIT == RightType) {
CurrUse = this->SetUseType(RightOp, CurrRT->GetOperatorType());
updated = true;
assert(CurrUse != this->GetLastUse());
break;
}
}
}
}
break;
case SMP_SUBTRACT:
case SMP_SUBTRACT_BORROW: // subtract with borrow
// Extract the current types of right and left operands and the operator.
OperType = CurrRT->GetOperatorType();
LeftOp = CurrRT->GetLeftOperand();
LeftUse = this->Uses.FindRef(LeftOp);
assert(LeftUse != this->GetLastUse()); // found it
LeftType = LeftUse->GetType();
if (CurrRT->HasRightSubTree()) {
updated |= this->InferOperatorType(CurrRT->GetRightTree());
RightType = CurrRT->GetRightTree()->GetOperatorType();
}
else {
RightOp = CurrRT->GetRightOperand();
if (o_void == RightOp.type) {
SMP_msg("ERROR: void operand in %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
return false;
}
else {
RightUse = this->Uses.FindRef(RightOp);
if (RightUse == this->GetLastUse()) {
SMP_msg("WARNING: Adding missing USE of ");
PrintOperand(RightOp);
SMP_msg(" in %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
this->Uses.SetRef(RightOp);
updated = true;
break;
}
else {
RightType = RightUse->GetType();
}
}
}
// If left operand is NUMERIC, operator is NUMERIC.
LeftNumeric = IsEqType(NUMERIC, LeftType);
RightNumeric = IsEqType(NUMERIC, RightType);
LeftPointer = IsDataPtr(LeftType);
RightPointer = IsDataPtr(RightType);
if (LeftNumeric) {
// Subtracting anything from a NUMERIC leaves it NUMERIC or NEGATEDPTR,
// in the special case in which a POINTER is subtracted from a NUMERIC.
// See NEGATEDPTR comments in the ADD/AND operators case above.
if (RightPointer) {
CurrRT->SetOperatorType(NEGATEDPTR, this);
updated = true;
}
else if (UNINIT == OperType) {
CurrRT->SetOperatorType(NUMERIC, this);
updated = true;
}
else if (IsNotEqType(NUMERIC, OperType) && IsNotEqType(NEGATEDPTR, OperType)) {
SMP_msg("ERROR: SMP_SUBTRACT from NUMERIC should be NUMERIC or NEGATEDPTR operator.");
SMP_msg(" Operator type is %d in: %s\n", OperType, DisAsmText.GetDisAsm(this->GetAddr()));
}
#if 0
if (!RightNumeric) {
// Right operand is being used as a NUMERIC, so propagate NUMERIC to it.
if (CurrRT->HasRightSubTree()) {
CurrRT->GetRightTree()->SetOperatorType(NUMERIC, this);
}
else {
RightUse = this->SetUseType(RightOp, NUMERIC);
}
updated = true;
}
#endif
} // end if LeftNumeric
else if (LeftPointer) {
if (UNINIT == OperType) {
// If we subtract another pointer type, we produce PTROFFSET.
if (RightPointer) {
CurrRT->SetOperatorType(PTROFFSET, this);
updated = true;
}
else if (RightType == PTROFFSET) {
// We assume B - (B - A) == A **!!**
CurrRT->SetOperatorType(POINTER, this);
SMP_msg("WARNING: PTR - PTROFFSET produces PTR at %x in %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
updated = true;
}
else if (RightNumeric) {
// pointer minus NUMERIC keeps same pointer type
CurrRT->SetOperatorType(LeftType, this);
updated = true;
}
}
else { // we have an operator type for the SMP_SUBTRACT
OperNumeric = IsEqType(NUMERIC, OperType);
OperPointer = IsDataPtr(OperType);
if (CurrRT->HasRightSubTree()) {
// Might need to iterate through the right tree again, if its operator
// can be typed.
if (UNINIT == RightType) {
if (OperPointer) {
// PTR := PTR - ?? ==> ?? is NUMERIC Why? ?? could be PTROFFSET
CurrRT->GetRightTree()->SetOperatorType(NUMERIC, this);
updated = true;
updated |= this->InferOperatorType(CurrRT->GetRightTree());
}
else if (OperType == PTROFFSET) {
// PTROFFSET := PTR - ?? ==> ?? is PTR
CurrRT->GetRightTree()->SetOperatorType(LeftType, this);
updated = true;
updated |= this->InferOperatorType(CurrRT->GetRightTree());
}
else if (OperNumeric) {
SMP_msg("WARNING: PTR - ?? produces NUMERIC at %x in %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
}
break;
}
else { // right operand; propagate operator type if needed
if (UNINIT == RightType) {
if (OperPointer) {
// PTR := PTR - ?? ==> ?? is NUMERIC Why? ?? could be PTROFFSET
RightUse = this->SetUseType(RightOp, NUMERIC);
updated = true;
assert(RightUse != this->GetLastUse());
}
else if (OperType == PTROFFSET) {
// PTROFFSET := PTR - ?? ==> ?? is PTR
RightUse = this->SetUseType(RightOp, LeftType);
updated = true;
}
else if (OperNumeric) {
SMP_msg("WARNING: PTR - ?? produces NUMERIC at %x in %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
break;
}
}
} // end if OperType is UNINIT ... else ...
} // end if LeftNumeric ... else if LeftPointer ...
else if (UNINIT == LeftType) {
// We make add-with-carry and subtract-with-borrow exceptions
// to the type propagation. LeftOp could have POINTER type
// inferred later; these instructions can change the type of
// the register from POINTER to NUMERIC, unlike regular
// add and subtract opcodes.
if ((UNINIT != OperType)
&& (SMP_SUBTRACT_BORROW != CurrOp)) {
LeftUse = this->SetUseType(LeftOp, OperType);
assert(LeftUse != this->GetLastUse());
updated = true;
}
}
break;
case SMP_ASSIGN:
// Extract the current types of right and left operands and SMP_ASSIGN operator.
OperType = CurrRT->GetOperatorType();
DefOp = CurrRT->GetLeftOperand();
CurrDef = this->Defs.FindRef(DefOp);
assert(CurrDef != this->GetLastDef()); // found it
LeftType = CurrDef->GetType();
if (CurrRT->HasRightSubTree()) {
updated |= this->InferOperatorType(CurrRT->GetRightTree());
RightType = CurrRT->GetRightTree()->GetOperatorType();
}
else {
UseOp = CurrRT->GetRightOperand();
if (o_void == UseOp.type) {
SMP_msg("ERROR: void operand for SMP_ASSIGN in %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
return false;
}
else {
CurrUse = this->Uses.FindRef(UseOp);
if (CurrUse == this->GetLastUse()) {
SMP_msg("WARNING: Adding missing USE of ");
PrintOperand(UseOp);
SMP_msg(" in %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
this->Uses.SetRef(UseOp);
updated = true;
break;
}
else {
RightType = CurrUse->GetType();
}
}
}
#if SMP_VERBOSE_DEBUG_INFER_TYPES
if (DebugFlag) {
SMP_msg("%x LeftType: %d OperatorType: %d RightType: %d\n", this->address, LeftType,
OperType, RightType);
}
#endif
if ((UNINIT == RightType) && (UNINIT == LeftType)) {
break;
}
else if (UNINIT == OperType) {
// UNINIT SMP_ASSIGN operator, but either LeftType or RightType is not UNINIT.
bool UpdatedOperType = false;
if (UNINIT != RightType) {
// We have to special case conditional moves. Only if both operands
// (the source and the prior value of the potential destination,
// which was added to the USE set by BuildMoveRTL()) agree in type
// can we propagate their common type to the operator and ultimately
// to the DEF.
if ((!this->MDIsConditionalMoveInstr()) || this->Uses.TypesAgreeNoFlags()) {
CurrRT->SetOperatorType(RightType, this);
updated = true;
OperType = RightType;
UpdatedOperType = true;
}
}
else { // LeftType must not be UNINIT
CurrRT->SetOperatorType(LeftType, this);
updated = true;
UpdatedOperType = true;
}
// Speed up type propagation by passing the RightType/OperType to the Def
// on this iteration.
if (UpdatedOperType) {
// Propagate the new DEF type unless it is an indirect memory access.
// Future: Propagate until re-DEF of addressing register terminates
// the propagation. **!!**
CurrDef = this->SetDefType(DefOp, OperType);
LeftType = OperType;
if (!MDIsIndirectMemoryOpnd(DefOp, this->BasicBlock->GetFunc()->UsesFramePointer())) {
bool IsMemOp = (o_reg != DefOp.type);
bool MemPropagate = MDIsStackAccessOpnd(DefOp, UseFP);
#if SMP_PROPAGATE_MEM_TYPES
;
#else
// Be conservative and only propagate register DEFs and SAFE stack locs.
// We can improve this in the future. **!!**
MemPropagate = MemPropagate && SafeFunc;
#endif
if ((o_reg == DefOp.type) || MemPropagate) {
int SSANum = CurrDef->GetSSANum();
if (this->BasicBlock->IsLocalName(DefOp)) {
(void) this->BasicBlock->PropagateLocalDefType(DefOp, LeftType,
this->GetAddr(), SSANum, IsMemOp);
}
else { // global name
this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false
(void) this->BasicBlock->PropagateGlobalDefType(DefOp, LeftType,
SSANum, IsMemOp);
}
}
}
}
break;
}
else if (UNINIT == LeftType) {
// SMP_ASSIGN operator has type, so propagate it.
CurrDef = this->SetDefType(DefOp, OperType);
LeftType = OperType;
updated = true;
// Propagate the new DEF type unless it is an indirect memory access.
// Future: Propagate until re-DEF of addressing register terminates
// the propagation. **!!**
if (!MDIsIndirectMemoryOpnd(DefOp, this->BasicBlock->GetFunc()->UsesFramePointer())) {
bool IsMemOp = (o_reg != DefOp.type);
bool MemPropagate = MDIsStackAccessOpnd(DefOp, UseFP);
#if SMP_PROPAGATE_MEM_TYPES
;
#else
// Be conservative and only propagate register DEFs and SAFE stack locs.
// We can improve this in the future. **!!**
MemPropagate = MemPropagate && SafeFunc;
#endif
if ((o_reg == DefOp.type) || MemPropagate) {
int SSANum = CurrDef->GetSSANum();
if (this->BasicBlock->IsLocalName(DefOp)) {
(void) this->BasicBlock->PropagateLocalDefType(DefOp, LeftType,
this->GetAddr(), SSANum, IsMemOp);
}
else { // global name
this->BasicBlock->GetFunc()->ResetProcessedBlocks(); // set Processed to false
(void) this->BasicBlock->PropagateGlobalDefType(DefOp, LeftType,
SSANum, IsMemOp);
}
}
}
break;
}
else if (UNINIT == RightType) {
// SMP_ASSIGN operator has type, so propagate it.
if (CurrRT->HasRightSubTree()) {
CurrRT->GetRightTree()->SetOperatorType(OperType, this);
updated = true;
updated |= this->InferOperatorType(CurrRT->GetRightTree());
}
else {
// For conditional moves, propagate to the pseudo-USE of the
// destination register as well as the source operand.
if (this->MDIsConditionalMoveInstr()) {
CurrUse = this->FindUse(DefOp);
assert(CurrUse != this->GetLastUse());
if (UNINIT == CurrUse->GetType())
CurrUse = this->SetUseType(DefOp, OperType);
else if (OperType != CurrUse->GetType()) {
SMP_msg("WARNING: Avoiding lattice oscillation from type %d to %d at %x for: ",
CurrUse->GetType(), OperType, this->address);
PrintOperand(CurrUse->GetOp());
SMP_msg("\n");
}
}
CurrUse = this->SetUseType(UseOp, OperType);
updated = true;
}
break;
}
break;
default:
SMP_msg("ERROR: Unknown operator %d at %x in %s\n",
CurrOp, this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
break;
} // end switch on operator
// Determine if type inference is finished for this register transfer.
if (updated && (!TypeInferenceFinished)) {
bool FinishedRight = false;
bool FinishedLeft = false;
bool FinishedOperator = (CurrRT->GetOperatorType() != UNINIT);
if (FinishedOperator) {
switch (CurrOp) {
case SMP_INPUT: // input from port
case SMP_OUTPUT: // output to port
case SMP_SIGN_EXTEND:
case SMP_ZERO_EXTEND:
case SMP_ADDRESS_OF: // take effective address
case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC
case SMP_BITWISE_NOT: // unary operator
case SMP_NEGATE: // unary negation
case SMP_DECREMENT:
case SMP_INCREMENT:
case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
// Unary operators have no right operand.
FinishedRight = true;
break;
default: // All binary operators come here
if (CurrRT->HasRightSubTree()) {
FinishedRight = CurrRT->GetRightTree()->IsTypeInferenceComplete();
}
else {
UseOp = CurrRT->GetRightOperand();
if (UseOp.type != o_void) {
CurrUse = this->Uses.FindRef(UseOp);
assert(CurrUse != this->GetLastUse());
FinishedRight = (CurrUse->GetType() != UNINIT);
}
else { // if o_void, no further type inference on it is possible.
FinishedRight = true;
}
}
break;
} // end switch on CurrOp
if (FinishedRight) { // no point checking left op if right op is not finished
DefOp = CurrRT->GetLeftOperand();
if (DefOp.type != o_void) {
if (SMP_ASSIGN == CurrOp) {
CurrDef = this->Defs.FindRef(DefOp);
assert(CurrDef != this->GetLastDef());
FinishedLeft = (CurrDef->GetType() != UNINIT);
}
else { // not ASSIGN, so really a UseOp not DefOp
CurrUse = this->Uses.FindRef(DefOp);
assert(CurrUse != this->GetLastUse());
FinishedLeft = (CurrUse->GetType() != UNINIT);
}
}
else { // if o_void, no further type inference on it is possible.
FinishedLeft = true;
}
}
TypeInferenceFinished = (FinishedLeft && FinishedRight);
} // end if (FinishedOperator)
} // end if (updated && (!TypeInferenceFinished))
if (TypeInferenceFinished) {
CurrRT->SetTypeInferenceComplete();
}
return updated;
} // end of SMPInstr::InferOperatorType()
// Transfer function: Does operator propagate signedness of its operands to its result?
bool SMPInstr::DoesOperatorTransferSign(SMPoperator CurrOp) {
bool transfer = false;
switch (CurrOp) {
case SMP_NULL_OPERATOR:
case SMP_CALL: // CALL instruction
case SMP_INPUT: // input from port
case SMP_OUTPUT: // output to port
case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC
case SMP_SIGNAL: // signal or raise exception
// No concept of signedness for some operators
break;
case SMP_ADDRESS_OF: // take effective address
case SMP_U_LEFT_SHIFT: // unsigned left shift
case SMP_U_RIGHT_SHIFT: // unsigned right shift
case SMP_ROTATE_LEFT:
case SMP_ROTATE_LEFT_CARRY: // rotate left through carry
case SMP_ROTATE_RIGHT:
case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry
case SMP_U_MULTIPLY:
case SMP_U_DIVIDE:
case SMP_U_REMAINDER:
case SMP_ZERO_EXTEND:
case SMP_BITWISE_NOT: // unary operator
case SMP_BITWISE_XOR:
case SMP_BITWISE_AND_NOT:
case SMP_S_COMPARE: // signed compare (subtraction-based)
case SMP_U_COMPARE: // unsigned compare (AND-based)
case SMP_GENERAL_COMPARE:
case SMP_S_LEFT_SHIFT: // signed left shift
case SMP_S_RIGHT_SHIFT: // signed right shift
case SMP_S_MULTIPLY:
case SMP_S_DIVIDE:
case SMP_SIGN_EXTEND:
case SMP_NEGATE: // unary negation
case SMP_LESS_THAN: // boolean test operators
case SMP_GREATER_THAN:
case SMP_LESS_EQUAL:
case SMP_GREATER_EQUAL:
case SMP_ENCRYPTION_OPERATION: // encryption or decryption bit manipulation operation
case SMP_EXTRACT_ZERO_EXTEND: // Extract sub-reg and zero-extend to reg length
// Inherently unsigned and signed operators force the signedness
// of their results, rather than propagating the signedness of
// their operands.
break;
case SMP_DECREMENT:
case SMP_INCREMENT:
case SMP_ADD:
case SMP_ADD_CARRY: // add with carry
case SMP_SUBTRACT:
case SMP_SUBTRACT_BORROW: // subtract with borrow
case SMP_ASSIGN:
case SMP_BITWISE_AND:
case SMP_BITWISE_OR:
case SMP_EQUAL:
case SMP_NOT_EQUAL:
case SMP_LOGICAL_AND:
case SMP_LOGICAL_OR:
case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
transfer = true;
break;
case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
case SMP_REVERSE_SHIFT_U: // Shift right operand by bit count in left operand
case SMP_SHUFFLE: // Shuffle bytes, words, etc. within destination operation per source mask
case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's
case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's
case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's
case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's
case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's
case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's
case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision
case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision
case SMP_AVERAGE_U: // Average of unsigned operands
case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate)
case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements
case SMP_MAX_S: // dest := signed_max(dest, src)
case SMP_MAX_U: // dest := unsigned_max(dest, src)
case SMP_MIN_S: // dest := signed_min(dest, src)
case SMP_MIN_U: // dest := unsigned_min(dest, src)
case SMP_ABSOLUTE_VALUE: // take absolute value
case SMP_CONVERT_INT_TO_FP: // convert integer to floating point
case SMP_CONVERT_FP_TO_INT: // convert floating point to integer
case SMP_CREATE_MASK: // Create AND-mask from operand and byte/word/dword position # in immediate
case SMP_INTERLEAVE: // extended-precision interleaving of bytes or words or dwords etc.; NUMERIC
case SMP_CONCATENATE: // extended-precision concatenation; NUMERIC
transfer = true;
break;
default:
SMP_msg("ERROR: Unknown operator in %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
break;
} // end switch on operator
return transfer;
} // end of SMPInstr::DoesOperatorTransferSign()
// Adjust signedness based on SSA def-use info.
void SMPInstr::MDFixupSignedness(void) {
set<DefOrUse, LessDefUse>::iterator DefIter = this->GetFirstNonFlagsDef();
set<DefOrUse, LessDefUse>::iterator UseIter;
unsigned short SignMask;
op_t DefOp = InitOp;
int DefSSANum;
int IdiomCode;
bool UnderflowOpcode = this->MDIsUnderflowingOpcode();
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if (DefIter != this->GetLastDef()) {
DefOp = DefIter->GetOp();
DefSSANum = DefIter->GetSSANum();
}
int TypeGroup = SMPTypeCategory[this->SMPcmd.itype];
if (this->IsNop())
TypeGroup = 1; // no-op idioms need their category reset
if (5 == TypeGroup) {
this->SetAddSubSourceType();
}
if (this->MDIsSignedLoad(SignMask)) {
// Compilers can generate zero-extensions just to clear unused bits in a register.
// We can wrongly infer UNSIGNED for the DEF as a result. The problem can be detected
// by seeing whether the zeroed-out bits are ever used. If all USEs of this register DEF
// only use the same number of bits as the source operand for the move, then the
// zero-extension bits do not mean anything, so we reset the signedness to unknown.
// This will hopefully not conflict with other sources of an UNSIGNED inference that
// could also have occurred (e.g. a MEDS type of POINTER is unlikely to occur with a
// zero-extended operand).
if (SignMask == FG_MASK_UNSIGNED) {
op_t MoveSource = this->GetMoveSource();
size_t SourceBitWidth = 8 * GetOpDataSize(MoveSource);
assert(DefIter != this->GetLastDef());
if (!(IsMemOperand(DefOp) || MDIsFlagsReg(DefOp))) {
CanonicalizeOpnd(DefOp);
int DefHashValue = HashGlobalNameAndSSA(DefOp, DefSSANum);
struct FineGrainedInfo DefUseFGInfo;
bool LocalName = this->BasicBlock->IsLocalName(DefOp);
if (LocalName) {
DefUseFGInfo = this->BasicBlock->GetUseFGInfo(DefHashValue);
}
else {
DefUseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(DefHashValue);
}
size_t DefUseMaxBitWidth = LargestBitWidthFromMask(DefUseFGInfo.SizeInfo);
if ((DefUseMaxBitWidth <= SourceBitWidth) && (0 != DefUseMaxBitWidth)) {
// We never use more bits than the original source had before it was zero-extended.
// We exclude the bitwidth==0 special case that signals that it was only used in
// a zero-extension or sign-extension move.
if (LocalName) {
this->BasicBlock->ClearDefSignedness(DefHashValue);
}
else {
this->BasicBlock->GetFunc()->ClearDefSignedness(DefHashValue);
}
}
}
}
}
// Second case: Subtraction of two values, each of which is just a condition code.
// This can happen in optimized code that tests for which of two condition codes
// has been set, e.g.:
// [arithmetic operation or comparison]
// seta al ; if "above" flag then al := 1 else al := 0
// setb bl ; if "below" flag then bl := 1 else bl := 0
// mov ecx,eax ; make copy of eax (including al) into ecx
// sub al,bl ; al := al - bl
// The resulting value in al will be 1 if the above flag was set, -1 if below, 0 if neither.
// We see the seta/setb as implying unsigned values, which is true, but if al becomes -1
// we do not want to consider this to be an underflow.
// The same logic applies to overflow opcodes: lea edx,[eax+eax-1] is hard to classify as
// potential underflow or overflow. If eax comes from a condition code (seta eax), the values
// will be small and no overflow check is needed.
int UseSSANum;
bool BenignUnderflow = false;
if (UnderflowOpcode && MDIsDataFlowOpnd(DefOp, UseFP)) {
UseIter = this->FindUse(DefOp); // DEF is also USE in subtraction
assert(UseIter != this->GetLastUse());
UseSSANum = UseIter->GetSSANum();
if (this->IsOpSourceConditionCode(DefOp, UseSSANum)) {
// If we had a decrement opcode, we are done, and underflow is benign.
if (this->MDIsDecrement()) {
BenignUnderflow = true;
}
else { // For subtraction, need to see if BOTH operands came from condition codes (flags).
op_t UseOp = this->GetUseOnlyAddSubOp();
if (MDIsDataFlowOpnd(UseOp, UseFP)) {
CanonicalizeOpnd(UseOp);
UseIter = this->FindUse(UseOp);
assert(UseIter != this->GetLastUse());
UseSSANum = UseIter->GetSSANum();
BenignUnderflow = this->IsOpSourceConditionCode(UseOp, UseSSANum);
}
}
}
if (BenignUnderflow) {
// We detected a subtraction or decrement that is permitted to produce a negative
// result. This means that the DEF is really SIGNED. We don't want to emit
// any underflow checks on this instruction, and we also want to propagate SIGNED
// as the type of DEF.
this->SetSuppressNumericAnnotation();
SignMask = FG_MASK_SIGNED;
struct FineGrainedInfo NewFG;
NewFG.SignMiscInfo = SignMask;
NewFG.SizeInfo = 0;
// Note that old FG info should be null at this point in FG inference,
// so we only have to pass in the SIGNED bits.
bool MapsChanged = this->UpdateDefOpFGInfo(DefOp, NewFG);
}
} // end if Underflow on register
if (this->MDIsDefiniteBenignUnderflowOpcode(IdiomCode) || this->MDIsSignBitFill()) {
// We have a pattern that changes sign, e.g. sbb edx,edx puts
// 0 or -1 in edx; likewise for sar edx,31. This needs no annotation.
this->SetSuppressNumericAnnotation();
// Furthermore, if we find add edx,positive_immediate_value
// in the SSA chain for this instruction, then we don't want
// to get false positives from the addition, which often changes
// the temporarily SIGNED value back to its original UNSIGNED:
// sbb edx,edx ; if edx is UNSIGNED, false positive when we produce -1.
// add edx,2 ; another false positive when we convert (0 or -1) to (1 or 2).
this->GetBlock()->SuppressAnnotOnSignChangingAddition(DefOp, DefSSANum, this->GetAddr());
}
return;
} // end of SMPInstr::MDFixupSignedness()
// Initial inferences (if any) about FG info of operand based solely on the RTL operator type above it in RTL.
bool SMPInstr::InitFGInfoFromOperator(SMPoperator CurrOp, struct FineGrainedInfo &InitFG) {
bool changed = false;
switch (CurrOp) {
case SMP_NULL_OPERATOR:
break;
case SMP_CALL: // CALL instruction
InitFG.SignMiscInfo |= FG_MASK_UNSIGNED; // target address is unsigned 32-bit
InitFG.SizeInfo |= (MD_NORMAL_BITWIDTH_MASK | FG_MASK_CODEPOINTER);
changed = true;
break;
case SMP_INPUT: // input from port
case SMP_OUTPUT: // output to port
case SMP_ADDRESS_OF: // take effective address
case SMP_U_COMPARE: // unsigned compare (AND-based)
case SMP_S_COMPARE: // signed compare (subtraction-based)
case SMP_GENERAL_COMPARE:
// NOTE: The AND-based and subtraction-based comparisons are used
// on lots of operands of all types, and the conditional jump that
// follows determines signedness, not the operator.
break;
case SMP_U_LEFT_SHIFT: // unsigned left shift
#if STARS_NO_SHIFT_SIGNEDNESS_IN_SCALEFACTOR
if (!this->MDIsLoadEffectiveAddressInstr()) {
// The left shift in the RTL for lea edx,[ebx+eax*4]
// does not imply that EAX is UNSIGNED. For other
// unsigned left shift RTLs, UNSIGNED is fine.
InitFG.SignMiscInfo |= FG_MASK_UNSIGNED;
changed = true;
}
break;
#endif
case SMP_U_RIGHT_SHIFT: // unsigned right shift
case SMP_ROTATE_LEFT:
case SMP_ROTATE_LEFT_CARRY: // rotate left through carry
case SMP_ROTATE_RIGHT:
case SMP_ROTATE_RIGHT_CARRY: // rotate right through carry
case SMP_U_MULTIPLY:
case SMP_U_DIVIDE:
case SMP_U_REMAINDER:
case SMP_ZERO_EXTEND:
case SMP_BITWISE_NOT: // unary operator
case SMP_BITWISE_XOR:
case SMP_BITWISE_AND_NOT:
InitFG.SignMiscInfo |= FG_MASK_UNSIGNED;
changed = true;
break;
case SMP_S_LEFT_SHIFT: // signed left shift
case SMP_S_RIGHT_SHIFT: // signed right shift
case SMP_S_MULTIPLY:
case SMP_S_DIVIDE:
case SMP_SIGN_EXTEND:
case SMP_NEGATE: // unary negation
case SMP_LESS_THAN: // boolean test operators
case SMP_GREATER_THAN:
case SMP_LESS_EQUAL:
case SMP_GREATER_EQUAL:
// Special case: If signed multiply operator, it might sometimes
// be used for unsigned operands when upper bits of the result
// are discarded, because there is no difference in the result bits
// between unsigned and signed multiplication when only the lower
// N bits are retained and the upper N bits are discarded.
if ((SMP_S_MULTIPLY == CurrOp) && (!(this->MDIsSignedArithmetic()))) {
break;
}
InitFG.SignMiscInfo |= FG_MASK_SIGNED;
changed = true;
break;
case SMP_DECREMENT:
case SMP_INCREMENT:
case SMP_ADD:
case SMP_ADD_CARRY: // add with carry
case SMP_SUBTRACT:
case SMP_SUBTRACT_BORROW: // subtract with borrow
case SMP_ASSIGN:
case SMP_BITWISE_AND:
case SMP_BITWISE_OR:
case SMP_EQUAL:
case SMP_NOT_EQUAL:
case SMP_LOGICAL_AND:
case SMP_LOGICAL_OR:
case SMP_UNARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_BINARY_NUMERIC_OPERATION: // miscellaneous; produces NUMERIC result
case SMP_SYSTEM_OPERATION: // for instructions such as CPUID, RDTSC, etc.; NUMERIC
case SMP_SHUFFLE: // Shuffle bytes, words, etc. within destination operation per source mask
case SMP_CREATE_MASK: // Create AND-mask from operand and byte/word/dword position # in immediate
case SMP_SIGNAL: // signal or raise exception
break;
case SMP_UNARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
case SMP_BINARY_FLOATING_ARITHMETIC: // all the same to our type system; all NUMERIC
InitFG.SignMiscInfo |= FG_MASK_SIGNED;
InitFG.SizeInfo |= FG_MASK_FLOAT_MMX;
changed = true;
break;
case SMP_REVERSE_SHIFT_U: // Shift right operand by bit count in left operand
case SMP_COMPARE_EQ_AND_SET: // Compare for equality and set fields to all 1's or all 0's
case SMP_COMPARE_NE_AND_SET: // Compare for inequality and set fields to all 1's or all 0's
case SMP_COMPARE_GT_AND_SET: // Compare for greater-than and set fields to all 1's or all 0's
case SMP_COMPARE_GE_AND_SET: // Compare for greater-than-or-equal and set fields to all 1's or all 0's
case SMP_COMPARE_LT_AND_SET: // Compare for less-than and set fields to all 1's or all 0's
case SMP_COMPARE_LE_AND_SET: // Compare for less-than-or-equal and set fields to all 1's or all 0's
case SMP_PACK_S: // Pack operands into extended-precision register, signed saturation for loss of precision
case SMP_MAX_S: // dest := signed_max(dest, src)
case SMP_MIN_S: // dest := signed_min(dest, src)
case SMP_ABSOLUTE_VALUE: // take absolute value
case SMP_CONVERT_INT_TO_FP: // convert integer to floating point
case SMP_CONVERT_FP_TO_INT: // convert floating point to integer
InitFG.SignMiscInfo |= FG_MASK_SIGNED;
InitFG.SizeInfo |= (FG_MASK_FLOAT_MMX | FG_MASK_BITWIDTH_128);
changed = true;
break;
case SMP_PACK_U: // Pack operands into extended-precision register, unsigned saturation for loss of precision
case SMP_AVERAGE_U: // Average of unsigned operands
case SMP_MULTIPLY_AND_ADD: // multiply and add (or multiply and accumulate)
case SMP_SUM_OF_DIFFS: // sum over two vectors of absolute values of differences of their elements
case SMP_MAX_U: // dest := unsigned_max(dest, src)
case SMP_MIN_U: // dest := unsigned_min(dest, src)
case SMP_ENCRYPTION_OPERATION: // encryption or decryption bit manipulation operation
case SMP_EXTRACT_ZERO_EXTEND: // Extract sub-reg and zero-extend to reg length
InitFG.SignMiscInfo |= FG_MASK_UNSIGNED;
InitFG.SizeInfo |= (FG_MASK_FLOAT_MMX | FG_MASK_BITWIDTH_128);
changed = true;
break;
case SMP_INTERLEAVE: // extended-precision interleaving of bytes or words or dwords etc.; NUMERIC
case SMP_CONCATENATE: // extended-precision concatenation; NUMERIC
InitFG.SignMiscInfo |= FG_MASK_SIGNED;
InitFG.SizeInfo |= (FG_MASK_FLOAT_MMX | FG_MASK_BITWIDTH_128);
changed = true;
break;
default:
SMP_msg("ERROR: Unknown operator in %s\n", DisAsmText.GetDisAsm(this->GetAddr()));
break;
} // end switch on operator
return changed;
} // end of SMPInstr::InitFGInfoFromOperator()
// Helper to take USE operand, find its SSANum, and return its UseHashValue.
int SMPInstr::GetUseOpHashAndSSA(op_t UseOp, int &SSANum) {
op_t SearchOp = UseOp;
CanonicalizeOpnd(SearchOp);
set<DefOrUse, LessDefUse>::iterator UseIter = this->FindUse(SearchOp);
assert(UseIter != this->GetLastUse());
SSANum = UseIter->GetSSANum();
int UseHashValue = HashGlobalNameAndSSA(SearchOp, SSANum);
return UseHashValue;
} // end of SMPInstr::GetUseOpHashAndSSA()
// Helper to take DEF operand, find its SSANum, and return its DefHashValue.
int SMPInstr::GetDefOpHashAndSSA(op_t DefOp, int &SSANum) {
op_t SearchOp = DefOp;
CanonicalizeOpnd(SearchOp);
set<DefOrUse, LessDefUse>::iterator DefIter = this->FindDef(SearchOp);
assert(DefIter != this->GetLastDef());
SSANum = DefIter->GetSSANum();
int DefHashValue = HashGlobalNameAndSSA(SearchOp, SSANum);
return DefHashValue;
} // end of SMPInstr::GetDefOpHashAndSSA()
// helper for InferOperatorFGInfo() to update DEF maps, return true if changed maps
bool SMPInstr::UpdateDefOpFGInfo(op_t DefOp, struct FineGrainedInfo NewFG) {
bool MapsChanged = false; // Changes to maps of name/SSA to FG info?
set<DefOrUse, LessDefUse>::iterator DefIter;
int SSANum;
int DefHashValue;
op_t SearchOp;
bool LocalName;
struct FineGrainedInfo OldFG, UnionFG;
if (o_reg == DefOp.type) {
// If operator is inherently signed, then we will have
// a sign bit set in NewFG from InitFGInfoFromOperator().
DefHashValue = this->GetDefOpHashAndSSA(DefOp, SSANum);
LocalName = this->BasicBlock->IsLocalName(DefOp);
if (LocalName) {
// Get old FG info from block level.
OldFG = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // global name
// Get old FG info from function level.
OldFG = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
// Get rid of stack access bits being passed around in InferOperatorFGInfo()
// for register operands in RTLs.
NewFG.SignMiscInfo &= FG_MASK_SIGNEDNESS_BITS;
UnionFG.SignMiscInfo = OldFG.SignMiscInfo | NewFG.SignMiscInfo;
UnionFG.SizeInfo = OldFG.SizeInfo | NewFG.SizeInfo;
if ((OldFG.SignMiscInfo != UnionFG.SignMiscInfo) || (OldFG.SizeInfo != UnionFG.SizeInfo)) {
// The signs they are a-changin' (or the sizes).
MapsChanged = true;
if (LocalName)
this->BasicBlock->UpdateDefFGInfo(DefHashValue, UnionFG);
else
this->BasicBlock->GetFunc()->UpdateDefFGInfo(DefHashValue, UnionFG);
}
}
else if (MDIsStackAccessOpnd(DefOp, this->GetBlock()->GetFunc()->UsesFramePointer())) {
ea_t InstAddr = this->GetAddr();
MapsChanged = this->GetBlock()->GetFunc()->MDUpdateFGStackLocInfo(InstAddr, DefOp, NewFG);
}
return MapsChanged;
} // end of SMPInstr::UpdateDefOpFGInfo()
// helper for InferOperatorFGInfo() to update USE maps, return true if changed maps
bool SMPInstr::UpdateUseOpFGInfo(op_t UseOp, struct FineGrainedInfo NewFG) {
bool MapsChanged = false; // Changes to maps of name/SSA to FG info?
int SSANum;
int UseHashValue;
bool LocalName;
struct FineGrainedInfo OldFG, UnionFG;
if (o_reg == UseOp.type) {
// If operator is inherently signed, then we will have
// a sign bit set in NewFG from InitFGInfoFromOperator().
UseHashValue = this->GetUseOpHashAndSSA(UseOp, SSANum);
LocalName = this->BasicBlock->IsLocalName(UseOp);
if (LocalName) {
// Get old FG info from block level.
OldFG = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // global name
// Get old FG info from function level.
OldFG = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
// Get rid of stack access bits being passed around in InferOperatorFGInfo()
// for register operands in RTLs.
NewFG.SignMiscInfo &= FG_MASK_SIGNEDNESS_BITS;
UnionFG.SignMiscInfo = OldFG.SignMiscInfo | NewFG.SignMiscInfo;
UnionFG.SizeInfo = OldFG.SizeInfo | NewFG.SizeInfo;
if ((OldFG.SignMiscInfo != UnionFG.SignMiscInfo) || (OldFG.SizeInfo != UnionFG.SizeInfo)) {
// The signs they are a-changin'.
MapsChanged = true;
if (LocalName)
this->BasicBlock->UpdateUseFGInfo(UseHashValue, UnionFG);
else
this->BasicBlock->GetFunc()->UpdateUseFGInfo(UseHashValue, UnionFG);
}
}
else if (MDIsStackAccessOpnd(UseOp, this->GetBlock()->GetFunc()->UsesFramePointer())) {
ea_t InstAddr = this->GetAddr();
MapsChanged = this->GetBlock()->GetFunc()->MDUpdateFGStackLocInfo(InstAddr, UseOp, NewFG);
}
return MapsChanged;
} // end of SMPInstr::UpdateUseOpFGInfo()
// Helper to fetch DEF signedness info for UseOp that has none.
unsigned short SMPInstr::GetDefSignInfoFromUseOp(op_t UseOp) {
int SSANum, UseHashValue;
bool LocalName;
UseHashValue = this->GetUseOpHashAndSSA(UseOp, SSANum);
LocalName = this->BasicBlock->IsLocalName(UseOp);
if (LocalName) {
// Get old sign info from block level.
return this->BasicBlock->GetDefSignMiscInfo(UseHashValue);
}
else { // global name
// Get old sign info from function level.
return this->BasicBlock->GetFunc()->GetDefSignMiscInfo(UseHashValue);
}
} // end of SMPInstr::GetDefSignInfoFromUseOp()
// infer FG info, + width on FirstIter; pass out FG info for op subtree, return true if change made to any FG info map.
bool SMPInstr::InferOperatorFGInfo(SMPRegTransfer *CurrRT, bool FirstIter, struct FineGrainedInfo &OpFG) {
bool MapsChanged = false; // Changes to maps of name/SSA to FG info?
bool NewChange = false; // Bit changes from InitFGInfoFromOperator() ?
SMPoperator CurrOp = CurrRT->GetOperator();
struct FineGrainedInfo LeftFG, OldLeftFG;
struct FineGrainedInfo RightFG, OldRightFG;
op_t LeftOp, RightOp;
unsigned short WidthMask, SignMask;
bool CurrOpTransfersSign = this->DoesOperatorTransferSign(CurrOp);
bool SpecialTransferCase = false;
bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
bool success;
int DefHashValue, UseHashValue, SSANum;
#if SMP_AGGRESSIVE_SIGN_TRANSFER
// Special case: If signed multiply operator, it might sometimes
// be used for unsigned operands when upper bits of the result
// are discarded, because there is no difference in the result bits
// between unsigned and signed multiplication when only the lower
// N bits are retained and the upper N bits are discarded.
// As a result, InitFGInfoFromOperator() did not set FG_MASK_SIGNED
// for the result of the multiplication. If we now detect that two
// unsigned operands are being multiplied together using the "signed"
// multiply operator, we need to transfer UNSIGNED to the result.
if ((SMP_S_MULTIPLY == CurrOp) && (!(this->MDIsSignedArithmetic()))) {
SpecialTransferCase = true;
}
#endif
// Recurse to the right first, so we can do a depth-first accumulation of FG info.
RightFG.SignMiscInfo = 0;
RightFG.SizeInfo = 0;
if (CurrRT->HasRightSubTree()) {
if (FirstIter) { // Get width as well as signedness
NewChange = this->InitFGInfoFromOperator(CurrOp, RightFG);
} // end if (FirstIter)
MapsChanged |= this->InferOperatorFGInfo(CurrRT->GetRightTree(), FirstIter, RightFG);
}
else {
RightOp = CurrRT->GetRightOperand();
if (RightOp.type == o_imm) {
// If immediate operand is a data address or code address, we can infer that it is unsigned.
uval_t ImmVal = RightOp.value;
if (IsImmedGlobalAddress((ea_t) ImmVal)) {
// Data address (type GLOBALPTR)
RightFG.SignMiscInfo |= FG_MASK_UNSIGNED;
}
else if (this->MDIsInterruptCall() || IsImmedCodeAddress((ea_t) ImmVal)) {
// Code address (type GLOBALPTR)
RightFG.SignMiscInfo |= FG_MASK_UNSIGNED;
}
}
else if ((RightOp.type == o_reg) && !RightOp.is_reg(MD_INSTRUCTION_POINTER_REG)) {
if (FirstIter) { // Get width as well as signedness
NewChange = this->InitFGInfoFromOperator(CurrOp, RightFG);
WidthMask = ComputeOperandBitWidthMask(RightOp, 0);
RightFG.SizeInfo |= WidthMask;
} // end if (FirstIter)
#if SMP_AGGRESSIVE_SIGN_TRANSFER
else {
// On all iterations other than 1st, see if USE has FG info.
UseHashValue = this->GetUseOpHashAndSSA(RightOp, SSANum);
if (this->BasicBlock->IsLocalName(RightOp)) {
// Get FG info from block.
RightFG = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else {
// Get FG info from function level.
RightFG = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
}
#endif
// Propagate signedness on all iterations.
// If operator is inherently signed, then we will have
// a sign bit set in RightFG from InitFGInfoFromOperator().
if ((RightFG.SignMiscInfo == 0) && (CurrOpTransfersSign || SpecialTransferCase)) {
// We have a USE with no sign info. See if we
// can get sign info from the DEF of this USE so we can
// transfer it up the RTL tree.
RightFG.SignMiscInfo =
(FG_MASK_SIGNEDNESS_BITS & (this->GetDefSignInfoFromUseOp(RightOp)));
}
if ((RightFG.SignMiscInfo != 0) || (RightFG.SizeInfo != 0))
MapsChanged |= this->UpdateUseOpFGInfo(RightOp, RightFG);
} // end if (RightOP is o_reg)
else if (MDIsStackAccessOpnd(RightOp, UseFP)) {
// We used to assume that all FG info transfers from stack locations to
// the target registers of stack loads happened in SMPInstr::MDSetWidthSignInfo(),
// in an early pass that needed no iteration. The FG info was loaded from the
// StackFGInfo that was computed in SMPFunction::FindOutgoingArgsSize() based solely
// on whether the load was sign-extended or zero-extended. Of course, many stack
// locations have neither kind of signed/unsigned load. So, if we see a store to
// a stack location with no signedness, we transfer the signedness of the RightFG
// to the stack location FGInfo in the code below that processes the LeftOp.
// As a result, we now have a need to examine regular loads from the stack to
// see if there is signedness info for the stack location.
success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, RightOp, RightFG);
if (success) {
SignMask = (RightFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
RightFG.SizeInfo = 0; // only want to transfer signedness
}
}
} // end if (right subtree) else right operand
LeftFG.SignMiscInfo = 0;
LeftFG.SizeInfo = 0;
LeftOp = CurrRT->GetLeftOperand();
bool OpIsDEF = (SMP_ASSIGN == CurrOp);
// Skip control-flow assignments to the instruction pointer register.
if ((LeftOp.type == o_reg) && !LeftOp.is_reg(MD_INSTRUCTION_POINTER_REG)) {
if (FirstIter) { // Get width as well as signedness
NewChange = this->InitFGInfoFromOperator(CurrOp, LeftFG);
// Special case: For sign-extended and zero-extended loads,
// we don't know whether the DEF will always be USEd as
// the smaller or larger size. For example, we could
// zero-extend a 16-bit stack location into a 32-bit register
// just because the compiler always loads unsigned shorts
// that way, but we might never use it as a 32-bit value.
// So there is no truncation if we store only 16 bits later.
// By setting the target of an extended load to zero width,
// we signal that we want the maximum USE width to determine
// whether the store is truncated (see EmitIntegerErrorAnnotations).
WidthMask = ComputeOperandBitWidthMask(LeftOp, 0);
if (OpIsDEF) {
if (this->MDIsSignedLoad(SignMask)) {
WidthMask = 0;
}
// DEF inherits sign from right hand side.
LeftFG.SignMiscInfo |= RightFG.SignMiscInfo;
}
else if ((LeftFG.SignMiscInfo == 0) && (CurrOpTransfersSign || SpecialTransferCase)) {
// We have a USE, not a DEF, with no sign info. See if we
// can get sign info from the DEF of this USE so we can
// transfer it up the RTL tree.
LeftFG.SignMiscInfo =
(FG_MASK_SIGNEDNESS_BITS & (this->GetDefSignInfoFromUseOp(LeftOp)));
}
LeftFG.SizeInfo |= WidthMask;
if ((LeftFG.SignMiscInfo != 0) || (LeftFG.SizeInfo != 0)) {
// Either NewChanged or CurrOpTransfersSign or SpecialTransferCase is true or we set WidthMask above.
// See if we would change the FG map entry.
if (OpIsDEF) { // Need DEF map info
MapsChanged |= this->UpdateDefOpFGInfo(LeftOp, LeftFG);
}
else { // need USE map info
MapsChanged |= this->UpdateUseOpFGInfo(LeftOp, LeftFG);
}
} // end if non-zero LeftFG info
} // end if (FirstIter)
#if SMP_AGGRESSIVE_SIGN_TRANSFER
else {
// On all iterations other than 1st, see if LeftOp has FG info.
if (!OpIsDEF) { // LeftOp is a USE
UseHashValue = this->GetUseOpHashAndSSA(LeftOp, SSANum);
if (this->BasicBlock->IsLocalName(LeftOp)) {
// Get FG info from block.
LeftFG = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else {
// Get FG info from function level.
LeftFG = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
}
else { // LeftOp is a DEF
DefHashValue = this->GetDefOpHashAndSSA(LeftOp, SSANum);
if (this->BasicBlock->IsLocalName(LeftOp)) {
// Get FG info from block.
LeftFG = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else {
// Get FG info from function level.
LeftFG = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
// See if RightFG has sign info to transfer to LeftFG.
SignMask = (RightFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
unsigned short LeftSignMask = (LeftFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
bool UpdateLeftSignMask = ((SignMask != 0) && (SignMask != LeftSignMask));
#if 1
if (OpIsDEF && (0 != LeftSignMask)) {
// We have a signedness value for the DEF operand. These values are
// highly reliable, e.g. MEDS type inference detects that the DEF is
// a POINTER and therefore it must be UNSIGNED, or the opcode always
// produces a SIGNED result, etc. We do not want to produce a mixed
// signedness DEF in these cases, regardless of what happens when
// we examine operands in the RTLs, because compiler code generation
// can produce screwy results that are less reliable than our early
// determinations of DEF signedness (e.g. compilers can zero-extend
// bytes even though they are signed, and hand-code assembly authors
// can sign-extend bytes that are unsigned and get away with it
// because the values are always very small and have a sign bit of zero.)
UpdateLeftSignMask = false;
}
#endif
if (UpdateLeftSignMask) {
// SignMask from RightFG has bits that will change LeftFG.SignMiscInfo.
LeftFG.SignMiscInfo |= SignMask;
MapsChanged |= this->UpdateDefOpFGInfo(LeftOp, LeftFG);
}
}
}
#endif
} // end of register case for LeftOp
else if (OpIsDEF && MDIsStackAccessOpnd(LeftOp, UseFP)
&& (!this->BasicBlock->GetFunc()->IsInOutgoingArgsRegion(LeftOp))) {
// For stores into the stack, if the operand being stored has signedness
// and the stack location has no signedness, then we have a case where
// none of the loads from the stack location were signed, so it is
// safe to infer signedness of the stack location based on what is being
// stored into it, as no store signedness will conflict with load signedness.
success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, LeftOp, LeftFG);
assert(success);
if (0 == (LeftFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS)) {
// No previous signedness info for the stack location.
// Get signedness info from RightFG.
SignMask = (RightFG.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
if ((0 != SignMask) && (FG_MASK_INCONSISTENT_SIGN != SignMask)) {
// Operand being stored has signedness.
// Transfer the signedness to the stack location.
struct FineGrainedInfo TempFG;
TempFG.SignMiscInfo = SignMask;
TempFG.SizeInfo = 0; // just update signedness
success = this->BasicBlock->GetFunc()->MDUpdateFGStackLocInfo(this->address, LeftOp, TempFG);
MapsChanged |= success;
}
}
}
// Prepare to return FG info for operator. First, OR the left and right FG infos.
if (NewChange || MapsChanged || CurrOpTransfersSign || SpecialTransferCase) {
OpFG.SignMiscInfo |= LeftFG.SignMiscInfo;
OpFG.SizeInfo |= LeftFG.SizeInfo;
OpFG.SignMiscInfo |= RightFG.SignMiscInfo;
OpFG.SizeInfo |= RightFG.SizeInfo;
}
// An operator could override the width or signedness info of its operands.
if (CurrOp == SMP_ADDRESS_OF) {
// Result is 32-bit data pointer.
OpFG.SizeInfo &= (~FG_MASK_BITWIDTH_FIELDS); // clear all width bits
OpFG.SizeInfo |= (FG_MASK_BITWIDTH_32 | FG_MASK_DATAPOINTER);
OpFG.SignMiscInfo &= (~FG_MASK_SIGNED);
OpFG.SignMiscInfo |= FG_MASK_UNSIGNED;
}
return MapsChanged;
} // end of SMPInstr::InferOperatorFGInfo()
// infer width on first pass, signedness on all passes
bool SMPInstr::InferFGInfo(unsigned short IterCount) {
bool MapsChanged = false; // Changes to maps of name/SSA to FG info?
struct FineGrainedInfo OpFG;
SMPitype DFType = this->GetDataFlowType();
assert(0 < IterCount); // start IterCount at 1, not 0.
if (DFType != DEFAULT) {
// We have a control flow instruction, e.g. call, return, branch, jump
// No data operands unless these instructions are indirect through a register,
// and the indirect operand is a memory operand in that case, e.g. [eax].
return MapsChanged;
}
if (1 == IterCount) {
// Fix signedness info now that complete SSA chains have been analyzed.
this->MDFixupSignedness();
}
for (size_t index = 0; index < this->RTL.GetCount(); ++index) {
SMPRegTransfer *CurrRT = this->RTL.GetRT(index);
if (SMP_NULL_OPERATOR == CurrRT->GetOperator()) // nothing to infer
continue;
OpFG.SignMiscInfo = 0;
OpFG.SizeInfo = 0;
MapsChanged |= this->InferOperatorFGInfo(CurrRT, (1 == IterCount), OpFG);
if (SMP_CALL == CurrRT->GetOperator()) // no LeftOp DEF
continue;
} // end for all RTs in the RTL
return MapsChanged;
} // end of SMPInstr::InferFGInfo()
// Get the meet of the metadata types of all non-flags DEFs.
SMPMetadataType SMPInstr::GetDefMetadataType(void) {
SMPMetadataType MeetType = DEF_METADATA_UNANALYZED;
set<DefOrUse, LessDefUse>::iterator CurrDef;
for (CurrDef = this->GetFirstDef(); CurrDef != this->GetLastDef(); ++CurrDef) {
SMPMetadataType CurrType;
op_t DefOp = CurrDef->GetOp();
if (DefOp.is_reg(X86_FLAGS_REG))
continue; // flags are always unused metadata; irrelevant
CurrType = CurrDef->GetMetadataStatus();
if (MeetType == CurrType)
continue; // no meet operation to perform
// Any time we find USED metadata, that overrides all other types.
if (CurrType == DEF_METADATA_USED)
return CurrType;
if (MeetType == DEF_METADATA_UNANALYZED)
MeetType = CurrType;
else if (MeetType < DEF_METADATA_REDUNDANT) {
// Conflict between types of different DEFs. It could be that
// a multiply or divide instruction DEFs EAX and EDX, and one
// of them is used in a store and the other is unused. In that
// case, the final MeetType is USED and we can return. Or, if
// one type is UNUSED and the other is REDUNDANT, we can set
// the final type to the REDUNDANT type and return. The USED case
// is handled above, so we must have the UNUSED vs. REDUNDANT case.
assert(CurrType >= DEF_METADATA_REDUNDANT);
MeetType = CurrType;
}
else { // MeetType REDUNDANT, not equal to CurrType.
if (CurrType >= DEF_METADATA_REDUNDANT) {
// One type is profile derived, both are REDUNDANT.
MeetType = DEF_METADATA_PROF_REDUNDANT;
}
else {
assert(DEF_METADATA_UNUSED == CurrType);
// leave MeetType as REDUNDANT
}
}
} // end for all DEFs
return MeetType;
} // end of SMPInstr::GetDefMetadataType()
// Are numeric values from a system call trusted input, so that all numeric errors
// derived from the returned values shouold be treated as benign?
bool SMPInstr::IsNumericTrustedSystemCall(void) {
bool TrustedCall = false;
if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) {
// We have a resolved call target address, either via direct or indirect call.
string FuncName = this->GetTrimmedCalledFunctionName();
TrustedCall = IsNumericSafeSystemCall(FuncName);
}
return TrustedCall;
} // end of SMPInstr::IsNumericTrustedSystemCall()
// Handle x86 opcode SIB byte annotations.
void SMPInstr::MDAnnotateSIBStackConstants(FILE *AnnotFile, op_t Opnd, ea_t offset, bool UseFP) {
int BaseReg;
int IndexReg;
ea_t displacement;
ushort ScaleFactor;
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
int SignedOffset = (int) offset;
MDExtractAddressFields(Opnd, BaseReg, IndexReg, ScaleFactor, displacement);
if (BaseReg == R_sp) { // ESP cannot be IndexReg
// ESP-relative constant offset
SMP_fprintf(AnnotFile,
"%10x %6d PTRIMMEDESP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm);
}
else if (UseFP && ((IndexReg == R_bp) || (BaseReg == R_bp))) {
// EBP-relative constant offset
SMP_fprintf(AnnotFile,
"%10x %6d PTRIMMEDEBP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm);
}
return;
} // end of MDAnnotateSIBStackConstants
// Emit annotations for constants used as ptr offsets from EBP or
// ESP into the stack frame. Only pay attention to EBP-relative
// offsets if EBP is being used as a frame pointer (UseFP == true).
void SMPInstr::AnnotateStackConstants(bool UseFP, FILE *AnnotFile) {
op_t Opnd;
ea_t offset;
int BaseReg;
int IndexReg;
ushort ScaleFactor;
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
int SignedOffset;
#if 0
if (this->address == 0x80925f4) {
SMP_msg("PROBLEM INSTRUCTION: \n");
this->PrintOperands();
}
#endif
for (int i = 0; i < UA_MAXOP; ++i) {
Opnd = this->SMPcmd.Operands[i];
if ((Opnd.type == o_displ) || (Opnd.type == o_phrase))
MDExtractAddressFields(Opnd, BaseReg, IndexReg, ScaleFactor, offset);
SignedOffset = (int) offset;
if (Opnd.type == o_displ) {
if (Opnd.hasSIB) {
MDAnnotateSIBStackConstants(AnnotFile, Opnd, offset, UseFP);
}
else { // no SIB
if (BaseReg == R_sp) {
// ESP-relative constant offset
SMP_fprintf(AnnotFile,
"%10x %6d PTRIMMEDESP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm);
}
else if (UseFP && (BaseReg == R_bp)) {
// EBP-relative constant offset
SMP_fprintf(AnnotFile,
"%10x %6d PTRIMMEDEBP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm);
}
} // end if (Opnd.hasSIB) ... else ...
} // end if (Opnd.type == o_displ)
else if (Opnd.type == o_phrase) {
offset = 0; // mmStrata thinks [esp] is [esp+0]
if (Opnd.hasSIB) {
MDAnnotateSIBStackConstants(AnnotFile, Opnd, offset, UseFP);
}
else { // Something like [ecx]; is it [esp] or [ebp] ?
if (BaseReg == R_sp) {
// ESP-relative constant offset
SMP_fprintf(AnnotFile,
"%10x %6d PTRIMMEDESP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm);
}
else if (UseFP && (BaseReg == R_bp)) {
// EBP-relative constant offset
SMP_fprintf(AnnotFile,
"%10x %6d PTRIMMEDEBP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, SignedOffset, disasm);
}
} // end if (Opnd.hasSIB) ... else ...
} // end else if (Opnd.type == o_phrase)
} // end for all operands
// If we move a stack pointer or frame pointer into another register, we
// need to annotate the implicit zero offset, e.g. mov edi,esp == mov edi,esp+0
// and edi is becoming a stack pointer that mmStrata needs to track.
if (this->MDIsStackPointerCopy(UseFP)) {
// Two possibilities: a move of the stack pointer, or an "lea"
// opcode, e.g. lea eax,[eap+8] ==> eax:=esp+8. In the move
// instruction (e.g. mov eax,esp), we have the implicit zero
// offset from the stack pointer register, but in the lea case,
// we might have zero or some other offset (lea eax,[esp] has
// the implicit zero).
int ESPoffset = 0;
if (NN_lea == this->SMPcmd.itype) {
ESPoffset = this->MDGetImmedUse();
}
// NOTE: Looks like this next line should be "else" because an lea instruction
// looks like it has a memory operand, hence it has already been handled above.
// We are getting duplicate annotations for lea instructions.
else {
if (UseFP && this->GetFirstUse()->GetOp().is_reg(R_bp)) {
SMP_fprintf(AnnotFile, "%10x %6d PTRIMMEDEBP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, ESPoffset, disasm);
}
else {
SMP_fprintf(AnnotFile, "%10x %6d PTRIMMEDESP STACK %d displ %s\n",
this->SMPcmd.ea, this->SMPcmd.size, ESPoffset, disasm);
}
}
}
return;
} // end of SMPInstr::AnnotateStackConstants()
// Emit all annotations for the instruction in the absence of RTL type inference.
void SMPInstr::EmitAnnotations(bool UseFP, bool AllocSeen, bool NeedsFrame, FILE *AnnotFile, FILE *InfoAnnotFile) {
ea_t addr = this->address;
flags_t InstrFlags = getFlags(addr);
bool MemDest = this->HasDestMemoryOperand();
bool MemSrc = this->HasSourceMemoryOperand();
bool SecondSrcOperandImmNum = this->IsSecondSrcOperandNumeric(InstrFlags); // assumes 2nd source is Imm or not-numeric?!
bool NoWarnFlag = false; // NOWARN annotation emitted?
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
#if SMP_CHILDACCESS_ALL_CODE
bool OrphanCode = (NULL == this->BasicBlock);
ProfilerInformation *ProfInfo = NULL;
if (!OrphanCode)
ProfInfo = this->BasicBlock->GetFunc()->GetProg()->GetProfInfo();
#endif
++OptCount[OptType]; // keep count for debugging info
#if SMP_ANNOTATE_ALL_MEMORY_OPERANDS
// Emit informational annotations for memory operands.
if (MemSrc) {
op_t MemSrcOp = this->MDGetMemUseOp();
size_t SrcBitWidth = 8 * GetOpDataSize(MemSrcOp);
op_t AnnotDefOp = MemSrcOp;
// Need to unnormalize stack memory DEFs and USEs before printing annotations.
this->MDGetUnnormalizedOp(AnnotDefOp);
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR MEMSRC %d", addr, this->SMPcmd.size, SrcBitWidth);
AnnotPrintOperand(AnnotDefOp, InfoAnnotFile);
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
if (MemDest) {
op_t MemDestOp = this->MDGetMemDefOp();
size_t DestBitWidth = 8 * GetOpDataSize(MemDestOp);
op_t AnnotDefOp = MemDestOp;
// Need to unnormalize stack memory DEFs and USEs before printing annotations.
this->MDGetUnnormalizedOp(AnnotDefOp);
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR MEMDEF %d", addr, this->SMPcmd.size, DestBitWidth);
AnnotPrintOperand(AnnotDefOp, InfoAnnotFile);
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
#endif
// If the instruction is a CALL (or INDIR_CALL that has been resolved to
// a single target address), then we need to see if the call is to a
// function that has been forbidden by a security policy. If so, we
// need to output a security alert.
// In the near future, we will output SPRI instrumentation to prevent
// the system/library call from executing.
if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) {
// We have a resolved call target address, either via direct or indirect call.
string FuncName = this->GetTrimmedCalledFunctionName();
ZST_SysCallType FuncCallType = GetCallTypeFromFuncName(FuncName);
ZST_Policy FuncCallPolicy = GetPolicyFromCallType(FuncCallType);
if (ZST_DISALLOW == FuncCallPolicy) {
if ((NULL != this->GetBlock()) && (NULL != this->GetBlock()->GetFunc())) {
SMP_fprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %x in %s\n",
FuncName.c_str(), this->address, this->GetBlock()->GetFunc()->GetFuncName());
}
else {
SMP_fprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %x\n", FuncName.c_str(), this->address);
}
SMP_fprintf(ZST_AlarmFile, "ALARM REASON: Call policy is DISALLOW for all calls of type %s\n", CallTypeNames[FuncCallType]);
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR SECURITYCALL Disallow 1 1 %s \n", addr, this->SMPcmd.size, disasm);
}
}
#if SMP_DEBUG_MEM
if (MemDest || MemSrc) {
SMP_msg("OptType: %d %s", OptType, disasm);
this->PrintOperands();
}
#endif
// Emit appropriate optimization annotations.
bool SDTInstrumentation = false;
switch (OptType) {
case 0: // SDT will have to handle these
{
#if SMP_DEBUG_TYPE0
SMP_msg("OptType 0: %x %s\n", addr, disasm);
#endif
// mmStrata wants to suppress warnings on the PUSH
// instructions that precede the LocalVarsAllocInstr
// (i.e. the PUSHes of callee-saved regs).
if ((!AllocSeen || !NeedsFrame) && this->MDIsPushInstr()) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL NoWarn %s \n",
addr, -3, disasm);
NoWarnFlag = true;
}
else {
SDTInstrumentation = true;
}
break;
}
case 1: // nothing for SDT to do
{ SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
break;
}
case 4: // INC, DEC, etc.: no SDT work unless MemDest
{ if (MemDest || MemSrc) {
SDTInstrumentation = true;
break; // treat as category 0
}
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL Always1stSrc %s \n",
addr, -1, disasm);
++AnnotationCount[OptType];
break;
}
case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work.
{ if (MemDest || MemSrc) {
SDTInstrumentation = true;
break; // treat as category 0
}
if (SecondSrcOperandImmNum
&& !this->MDIsFrameAllocInstr()
#if SPECIAL_CASE_CARRY_BORROW
&& (this->SMPcmd.itype != NN_adc)
&& (this->SMPcmd.itype != NN_sbb)
#endif
) { // treat as category 1
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
}
else {
SDTInstrumentation = true;
}
break;
}
case 6: // Only OS code should include these; problem for SDT
{ if (MemDest) {
SDTInstrumentation = true;
break; // treat as category 0
}
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL AlwaysPTR %s \n",
addr, -OptType, disasm);
++AnnotationCount[OptType];
break;
}
case 8: // Implicitly writes to EDX:EAX, always numeric.
{ SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ZZ %s %s \n",
addr, -2, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
SDTInstrumentation = true;
break;
}
case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0)
{ if (MemDest) {
#if SMP_DEBUG2
// MemDest seems to happen too much.
SMP_msg("Floating point MemDest: %s \n", disasm);
#endif
SDTInstrumentation = true;
break; // treat as category 0
}
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
break;
}
case 10: // Implicitly writes to EDX:EAX and ECX, always numeric.
{ SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ECX ZZ %s %s \n",
addr, -2, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
SDTInstrumentation = true;
break;
}
default: // 2,3,7: Optimization possibilities depend on operands
{
#if SMP_DEBUG2
if (OptType == 3) { // MOV instr class
if (MemDest) {
SMP_msg("MemDest on MOV: %s\n", disasm);
}
else if (!SecondSrcOperandNum) {
SMP_msg("MOV: not 2nd op numeric: %s\n", disasm);
this->PrintOperands();
}
}
#endif
SDTInstrumentation = true;
if (MemDest) {
#if SMP_DEBUG_XOR
if (OptType == 2)
SMP_msg("MemDest on OptType 2: %s\n", disasm);
#endif
break; // treat as category 0
}
if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s %s %s \n",
addr, -2, this->DestString(OptType),
OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
}
break;
}
} // end switch (OptType)
// always emit stack constant annotations, in case strata is
// instrumenting all instructions, or trying to verify speculative annotations.
this->AnnotateStackConstants(UseFP, AnnotFile);
// If mmStrata is going to have to deal with the
// instruction, then we can annotate EBP and ESP
// relative constant offsets. If we have emitted
// an annotation of type -1, there is no point
// in telling mmStrata about these constants.
// Likewise, we can tell mmStrata if a MemDest is an
// non-directly-accessed child object.
if (SDTInstrumentation || NoWarnFlag) {
if (this->DeadRegsBitmap.any()) {
// Optimize by informing mmStrata of dead registers. It can avoid saving
// and restoring dead state. This is particularly important for EFLAGS,
// as restoring the flags is a pipeline serializing instruction.
SMP_fprintf(AnnotFile, "%10x %6d INSTR DEADREGS ",
addr, this->SMPcmd.size);
this->PrintDeadRegs(AnnotFile);
SMP_fprintf(AnnotFile, " %s \n", disasm);
}
#if SMP_CHILDACCESS_ALL_CODE
int ChildOffset, ChildSize;
if (MemDest && !OrphanCode
&& ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr,
ChildOffset, ChildSize)) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR CHILDACCESS %d %d ZZ %s \n",
addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm);
}
#endif
}
return;
} // end of SMPInstr::EmitAnnotations()
/**
* Emits Safe Returns
* Mark the type of the annotation as "-4". Currently the SDT is ignoring this
* annotation.
*/
void SMPInstr::EmitSafeReturn(FILE *AnnotFile)
{
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL SafeReturn %s\n",
this->address, -4, disasm);
}
// Emit all annotations for the instruction using RTL type inference.
void SMPInstr::EmitTypeAnnotations(bool UseFP, bool AllocSeen, bool NeedsFrame, FILE *AnnotFile, FILE *InfoAnnotFile) {
ea_t addr = this->address;
flags_t InstrFlags = getFlags(addr);
int TypeGroup = SMPTypeCategory[this->SMPcmd.itype];
bool NumericDEFs = this->AllDefsNumeric(); // all DEFs are NUMERIC or CODEPTR
bool ProfiledDEFs = this->AnyDefsProfiled(); // Some DEFs come from the profiler
bool UnusedMetadata = this->AllDefMetadataUnused();
bool MemDest = this->HasDestMemoryOperand();
bool MemSrc = this->HasSourceMemoryOperand();
bool SecondSrcOperandImmNum = this->IsSecondSrcOperandNumeric(InstrFlags); // assumes 2nd source is imm or not-numeric??
bool NoWarnFlag = false; // NOWARN annotation emitted?
bool CarryBorrow = ((this->SMPcmd.itype == NN_adc)
|| (this->SMPcmd.itype == NN_sbb));
// Do we have the special case in which a non-NUMERIC comes into
// an add with carry or subtract with borrow and the result
// has been inferred to be NUMERIC?
bool TypeChange = CarryBorrow && (!IsNumeric(this->AddSubUseType))
&& NumericDEFs;
SMPMetadataType DefMetadataType = this->GetDefMetadataType();
ProfilerInformation *ProfInfo;
ProfInfo = this->BasicBlock->GetFunc()->GetProg()->GetProfInfo();
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
++OptCount[this->OptType]; // keep count for debugging info
if (this->IsNop())
TypeGroup = 1; // no-op idioms need their category reset
// Emit appropriate optimization annotations.
bool SDTInstrumentation = false;
// In case we short circuit this function due to redundant or unused metadata, etc.,
// perform analyses that are useful to integer error annotations.
#if SMP_ANNOTATE_ALL_MEMORY_OPERANDS
// Emit informational annotations for memory operands.
if (MemSrc) {
op_t MemSrcOp = this->MDGetMemUseOp();
size_t SrcBitWidth = 8 * GetOpDataSize(MemSrcOp);
op_t AnnotDefOp = MemSrcOp;
// Need to unnormalize stack memory DEFs and USEs before printing annotations.
this->MDGetUnnormalizedOp(AnnotDefOp);
SMP_fprintf(InfoAnnotFile, "%10x %6zu INSTR MEMSRC %zu", addr, this->SMPcmd.size, SrcBitWidth);
AnnotPrintOperand(AnnotDefOp, InfoAnnotFile);
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
if (MemDest) {
op_t MemDestOp = this->MDGetMemDefOp();
size_t DestBitWidth = 8 * GetOpDataSize(MemDestOp);
op_t AnnotDefOp = MemDestOp;
// Need to unnormalize stack memory DEFs and USEs before printing annotations.
this->MDGetUnnormalizedOp(AnnotDefOp);
SMP_fprintf(InfoAnnotFile, "%10x %6zu INSTR MEMDEF %zu", addr, this->SMPcmd.size, DestBitWidth);
AnnotPrintOperand(AnnotDefOp, InfoAnnotFile);
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
#endif
// If the instruction is a CALL (or INDIR_CALL that has been resolved to
// a single target address), then we need to see if the call is to a
// function that has been forbidden by a security policy. If so, we
// need to output a security alert.
// In the near future, we will output SPRI instrumentation to prevent
// the system/library call from executing.
if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) {
// We have a resolved call target address, either via direct or indirect call.
string FuncName = this->GetTrimmedCalledFunctionName();
ZST_SysCallType FuncCallType = GetCallTypeFromFuncName(FuncName);
ZST_Policy FuncCallPolicy = GetPolicyFromCallType(FuncCallType);
if (ZST_DISALLOW == FuncCallPolicy) {
SMP_fprintf(ZST_AlarmFile, "ALARM: Call to %s will be disallowed at %x in %s\n",
FuncName.c_str(), this->address, this->GetBlock()->GetFunc()->GetFuncName());
SMP_fprintf(ZST_AlarmFile, "ALARM REASON: Call policy is DISALLOW for all calls of type %s\n", CallTypeNames[FuncCallType]);
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR SECURITYCALL Disallow 1 1 %s \n", addr, this->SMPcmd.size, disasm);
}
}
// If the DEF metadata is all unused, mmStrata can skip the instruction.
// We omit this for groups 1 and 14, so that the metadata analysis
// does not get statistical credit for instructions that were already
// getting -1 annotations without analysis.
// We also cannot skip NN_adc and NN_sbb instructions that change the
// type of the incoming register.
if ((1 != TypeGroup) && (14 != TypeGroup) && (!this->MDIsInterruptCall())
&& !TypeChange) {
if (UnusedMetadata) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataUnused %s \n",
addr, -1, disasm);
++AnnotationCount[this->OptType];
return;
}
else if (DEF_METADATA_REDUNDANT == DefMetadataType) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataRedundant %s \n",
addr, -1, disasm);
++AnnotationCount[this->OptType];
return;
}
else if (DEF_METADATA_PROF_REDUNDANT == DefMetadataType) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL MetadataRedundant %s \n",
addr, -257, disasm);
++AnnotationCount[this->OptType];
// Profiler annotations could be backed off due to false
// positives, in which case we will need stack constant
// annotations.
this->AnnotateStackConstants(UseFP, AnnotFile);
return;
}
}
switch (TypeGroup) {
case 0: // SDT will have to handle these
case 11: // PUSH/POP **!!** What if we push/pop NUMERIC type? Optimize?
// --jdh
// pop numeric's can be optimized with a numericdef annotation.
// numeric push's can't immediately be optimized, but if the stack location
// can be proven as dead metadata, then perhaps optimize.
// --jdh
// mmStrata wants to suppress warnings on the PUSH
// instructions that precede the LocalVarsAllocInstr
// (i.e. the PUSHes of callee-saved regs).
if ((!AllocSeen || !NeedsFrame) && this->MDIsPushInstr()) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL NoWarn %s \n",
addr, -3, disasm);
NoWarnFlag = true;
}
else if (this->MDIsPopInstr() && NumericDEFs) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType),
disasm);
++AnnotationCount[this->OptType];
}
else {
SDTInstrumentation = true;
}
break;
case 1: // nothing for SDT to do
case 14:
if (MemDest) {
SMP_msg("ERROR: MemDest in Type Category 1 or 14: %x %s\n", addr, disasm);
SDTInstrumentation = true;
break;
}
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
break;
case 4: // INC, DEC, etc.: no SDT work unless MemDest
if (MemDest || MemSrc) { // pretty conservative here?
// could be more aggressive if we know there's no overflow. -- jdh
SDTInstrumentation = true;
break; // treat as category 0
}
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL Always1stSrc %s \n",
addr, -1, disasm);
++AnnotationCount[this->OptType];
break;
case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work.
#if 1
if (MemDest) {
SDTInstrumentation = true;
break; // treat as category 0
}
#endif
if (SecondSrcOperandImmNum
&& !this->MDIsFrameAllocInstr()
&& !TypeChange
#if SPECIAL_CASE_CARRY_BORROW
&& (!CarryBorrow)
#endif
) {
// treat as category 1
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
}
else if (IsEqType(NUMERIC, this->AddSubSourceType)
&& !this->MDIsFrameAllocInstr()
&& !TypeChange
#if SPECIAL_CASE_CARRY_BORROW
&& (!CarryBorrow)
#endif
) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL 2ndSrcNumeric %s \n",
addr, -1, disasm);
++AnnotationCount[this->OptType];
}
else if (NumericDEFs) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
++AnnotationCount[this->OptType];
}
#if SMP_OPTIMIZE_ADD_TO_NUMERIC
else if ((NN_add == this->SMPcmd.itype) && (!MemSrc)
&& IsNumeric(this->AddSubUseType)) {
// reg1 := reg1 + reg2, where reg1 comes in as NUMERIC,
// means that reg1 will get DEFed to the type of reg2,
// whatever it is. If reg2 were known to be NUMERIC,
// we would have hit one of the annotation cases above.
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s := %s ZZ AddToNumeric %s \n",
addr, -5, RegNames[this->AddSubUseOp.reg],
RegNames[this->AddSubSourceOp.reg], disasm);
++AnnotationCount[this->OptType];
}
#endif
else {
SDTInstrumentation = true;
}
break;
case 6: // Only OS code should include these; problem for SDT
if (MemDest) {
SDTInstrumentation = true;
break; // treat as category 0
}
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL AlwaysPTR %s \n",
addr, -OptType, disasm);
++AnnotationCount[this->OptType];
break;
case 8: // Implicitly writes to EDX:EAX, always numeric.
if (this->OptType == 10) { // writes to ECX also
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ECX ZZ %s %s \n",
addr, -2, OptExplanation[this->OptType], disasm);
}
else {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n EDX EAX ZZ %s %s \n",
addr, -2, OptExplanation[this->OptType], disasm);
}
++AnnotationCount[this->OptType];
SDTInstrumentation = true;
break;
case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0)
if (MemDest) {
SDTInstrumentation = true;
#if 0
if (NumericDEFs) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
++AnnotationCount[this->OptType];
}
#endif
}
else {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
}
break;
case 10: // AND, OR, etc.: If all DEFs have been inferred to be
// NUMERIC, then output optimizing annotation.
SDTInstrumentation = true;
if (MemDest) { // **!!** optimize with numeric annotation in future
break; // treat as category 0
}
else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
++AnnotationCount[this->OptType];
}
break;
case 12: // Exchange, exchange and add, conditional exchange: All NUMERIC
// sources ==> NUMERIC DEFs, so nothing for mmStrata to do.
if (MemDest) { // **!!** optimize with numeric annotation in future
SDTInstrumentation = true;
break; // treat as category 0
}
else if (NumericDEFs) { // NUMERIC result because of NUMERIC sources
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n", addr,
ProfiledDEFs ? -256-1 : -1, OptExplanation[TypeGroup], disasm);
++AnnotationCount[this->OptType];
}
else
SDTInstrumentation = true;
break;
case 13:
case 15: // Floating point, NUMERIC, possible memory destination.
// If not memory destination, fpreg dest, so nothing for mmStrata to do.
if (MemDest) { // **!!** optimize with numeric annotation in future
SDTInstrumentation = true;
break; // treat as category 0
}
else { // NUMERIC floating register result; these regs are always NUMERIC
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL %s %s \n", addr,
-1, OptExplanation[TypeGroup], disasm);
++AnnotationCount[this->OptType];
}
break;
default: // 2,3,7: Optimization possibilities depend on operands
SDTInstrumentation = true;
if (MemDest) {
break; // treat as category 0
}
if ((OptType == 2) || (OptType == 7) || SecondSrcOperandImmNum) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s %s %s \n",
addr, -2, this->DestString(this->OptType),
OptExplanation[this->OptType], disasm);
++AnnotationCount[this->OptType];
}
else if (NumericDEFs) { // NUMERIC move instruction
SMP_fprintf(AnnotFile, "%10x %6d INSTR LOCAL n %s NumericDEFs %s \n",
addr, ProfiledDEFs ? -256-2 : -2, this->DestString(this->OptType), disasm);
++AnnotationCount[this->OptType];
}
break;
} // end switch (OptType)
// always annotate stack constants for the profiler, etc.
this->AnnotateStackConstants(UseFP, AnnotFile);
// If mmStrata is going to have to deal with the
// instruction, then we can annotate EBP and ESP
// relative constant offsets. If we have emitted
// an annotation of type -1, there is no point
// in telling mmStrata about these constants.
// Likewise, we can tell mmStrata if a MemDest is an
// non-directly-accessed child object.
if (SDTInstrumentation || NoWarnFlag) {
if (this->DeadRegsBitmap.any()) {
// Optimize by informing mmStrata of dead registers. It can avoid saving
// and restoring dead state. This is particularly important for EFLAGS,
// as restoring the flags is a pipeline serializing instruction.
SMP_fprintf(AnnotFile, "%10x %6d INSTR DEADREGS ",
addr, this->SMPcmd.size);
this->PrintDeadRegs(AnnotFile);
SMP_fprintf(AnnotFile, " %s \n", disasm);
}
int ChildOffset, ChildSize;
if (MemDest && ProfInfo->GetMemoryAccessInfo()->ComputeNonDirectAccessRegion(addr,
ChildOffset, ChildSize)) {
SMP_fprintf(AnnotFile, "%10x %6d INSTR CHILDACCESS %d %d ZZ %s \n",
addr, this->SMPcmd.size, ChildOffset, ChildSize, disasm);
}
#if SMP_IDENTIFY_POINTER_ADDRESS_REG
// WARNING: This old code was written prior to the normalization of stack operands.
if (MemDest) {
assert(this->HasDestMemoryOperand());
set<DefOrUse, LessDefUse>::iterator PtrUse;
PtrUse = this->GetPointerAddressReg(this->DEFMemOp);
if (PtrUse != this->GetLastUse()) { // found POINTER addr reg USE
if (PtrUse->GetOp().type == o_reg) {
ushort PtrReg = PtrUse->GetOp().reg;
SMP_fprintf(AnnotFile, "%10x %6d INSTR POINTER reg %s ZZ %s \n",
addr, this->SMPcmd.size, RegNames[PtrReg], disasm);
}
}
}
#endif
}
return;
} // end of SMPInstr::EmitTypeAnnotations()
// union of sign masks from 2 reg USEs for binary arithmetic
unsigned short SMPInstr::SignMaskUnionFromUseRegs(void) {
unsigned short UnionMask = 0;
unsigned short UseSignMask, DefSignMask;
set<DefOrUse, LessDefUse>::iterator UseIter;
op_t UseOp;
size_t RegOpCount = 0;
int UseHashValue;
struct FineGrainedInfo UseFGInfo, DefFGInfo;
bool UseFP = this->GetBlock()->GetFunc()->UsesFramePointer();
if (this->HasSourceMemoryOperand()) {
// Don't have binary arithmetic on just registers.
return UnionMask;
}
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseOp = UseIter->GetOp();
if (MDIsGeneralPurposeReg(UseOp) && (!MDIsStackPtrReg(UseOp.reg, UseFP))) {
++RegOpCount;
UseHashValue = HashGlobalNameAndSSA(UseOp, UseIter->GetSSANum());
bool LocalUseName = this->BasicBlock->IsLocalName(UseOp);
if (LocalUseName) {
// Local name, find in basic block maps.
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
UseSignMask = (FG_MASK_SIGNEDNESS_BITS & UseFGInfo.SignMiscInfo);
if (0 == UseSignMask) {
// Try to get signedness from DEF.
if (LocalUseName) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
}
DefSignMask = (FG_MASK_SIGNEDNESS_BITS & DefFGInfo.SignMiscInfo);
UnionMask |= DefSignMask;
}
else {
UnionMask |= UseSignMask;
}
}
}
if (2 > RegOpCount) { // only interested in binary arithmetic on two registers
UnionMask = 0;
}
return UnionMask;
} // SMPInstr::SignMaskUnionFromUseRegs()
// emit check annotations for signedness, overflow, truncation, etc.
void SMPInstr::EmitIntegerErrorAnnotations(FILE *InfoAnnotFile, list<size_t> &LoopList) {
set<DefOrUse, LessDefUse>::iterator UseIter, DefIter;
op_t UseOp, DefOp;
unsigned short UseWidthInfo, DefWidthInfo, SourceDefWidthInfo;
unsigned short DefSignInfo, SourceDefSignInfo;
unsigned short UseSignMask, DefSignMask, SourceDefSignMask;
struct FineGrainedInfo UseFGInfo, DefFGInfo, SourceDefFGInfo;
size_t UseBitWidth, DefBitWidth, UseMaxBitWidth, SourceDefBitWidth, DefMaxBitWidth;
ea_t DefAddr;
int UseHashValue, DefHashValue, SSANum, DefSSANum, UseSSANum;
int IdiomCode = 0; // records code pattern number causing suppression of annotation.
bool OverflowOpcode = this->MDIsOverflowingOpcode();
bool UnderflowOpcode = this->MDIsUnderflowingOpcode();
bool CheckForOverflow;
bool UseIsSigned, DefIsSigned, UseIsUnsigned, DefIsUnsigned, SourceDefIsSigned, SourceDefIsUnsigned;
bool UseSignMixed, SourceDefSignMixed; // inconsistent signedness
bool UseFP = this->BasicBlock->GetFunc()->UsesFramePointer();
bool SuppressSignednessCheck = false; // If we are not confident in check, set to true.
bool PartialStore; // Store is fewer bits than is defined for the target, e.g. overwriting last 8 bits
// of an int or a pointer. Cannot have signedness error in that case, as sign bit
// is not affected.
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
string SinkString("");
if (this->IsNumericAnnotationSuppressed()) {
return; // Cannot figure out IdiomCode; suppressed from earlier instruction's analyses
}
// If the instruction is the beginning of an infinite loop, we want no annotations other than
// the infinite loop annotation.
if (this->IsFirstInBlock()) {
if (this->GetBlock()->IsInfiniteSelfLoop()) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR INFINITELOOP %s \n",
this->address, this->SMPcmd.size, disasm);
return;
}
}
// Case 1: Overflow on addition.
// Case 2: Underflow on subtraction.
if (OverflowOpcode || UnderflowOpcode) {
// If the flags register DEF is dead, we need a CHECK OVERFLOW/UNDERFLOW annotation.
DefOp = InitOp;
DefOp.type = o_reg;
DefOp.reg = MD_FLAGS_REG;
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
if (this->BasicBlock->IsDefDead(this->address, DefOp)) {
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
DefOp = DefIter->GetOp();
SSANum = DefIter->GetSSANum();
SMPOperandType DefType = DefIter->GetType();
SMPOperandType MemType;
bool IgnoreOverflow = this->IsBenignOverflow(IdiomCode);
uval_t ConstValue = 0;
// NOTE: We no longer suppress the IgnoreOverflow annotations. Instead, we print
// them with an IdiomCode so that suppression can happen downstream, along with
// customized continuation policies, statistical summarizing, etc.
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
if (IgnoreOverflow)
++BenignOverflowInstCount;
#endif
// Don't worry about stack space allocation instructions. The
// program will crash long before the stack pointer underflows
// below zero.
if (!((o_reg == DefOp.type) && DefOp.is_reg(MD_STACK_POINTER_REG))) {
if (!IgnoreOverflow && (this->GetBlock()->IsBenignOverflowDEF(DefOp, SSANum, this->GetAddr(), UnderflowOpcode, IdiomCode))) {
IgnoreOverflow = true;
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++BenignOverflowDefCount;
#endif
}
if (o_reg == DefOp.type) {
DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum);
if (this->BasicBlock->IsLocalName(DefOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
}
else if (MDIsStackAccessOpnd(DefOp, UseFP)) {
bool success = this->BasicBlock->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo);
assert(success);
}
else { // non-stack memory address; we know nothing about it.
DefFGInfo.SignMiscInfo = 0;
DefFGInfo.SizeInfo = 0;
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++NumericAnnotationsCount12;
#endif
DefSignInfo = DefFGInfo.SignMiscInfo;
DefSignMask = DefSignInfo & FG_MASK_SIGNEDNESS_BITS;
DefWidthInfo = DefFGInfo.SizeInfo;
DefBitWidth = LargestBitWidthFromMask(DefWidthInfo);
if (0 == DefBitWidth) {
// Could happen for non-stack memory operands, for example.
DefBitWidth = MD_NORMAL_MACHINE_BITWIDTH;
}
if (DefSignMask == 0) {
// If no sign info from DEF, see if we can get it from the USE regs.
DefSignMask = this->SignMaskUnionFromUseRegs();
}
if ((0 == IdiomCode) && (DefSignMask == FG_MASK_UNSIGNED)) {
// UNSIGNED is often reliable because compilers zero-extend values
// just so they can avoid having undefined register bits. Later, only
// the lower bits get stored. We already detect this pattern as a benign
// truncation, but we need to back off on the certainty of our UNSIGNED
// inference on overflow/underflow operations along the path.
UseIter = this->FindUse(DefOp);
if (UseIter != this->GetLastUse()) {
UseOp = UseIter->GetOp();
UseSSANum = UseIter->GetSSANum();
if ((o_reg == UseOp.type) || MDIsStackAccessOpnd(UseOp, UseFP)) {
if (this->IsOpSourceZeroExtendedMove(UseOp, UseSSANum, false)) {
if (this->GetBlock()->IsOpDestTruncatedWrite(DefOp, SSANum, this->GetAddr())) {
// Operand source was reduced-width, zero-extended; ultimate use is to store
// only the lower bits. The zero extension is no longer a reliable source of
// inference for UNSIGNEDness.
DefSignMask = FG_MASK_INCONSISTENT_SIGN;
}
}
else if (UnderflowOpcode && this->IsOpSourceSmallPositiveConstant(UseOp, UseSSANum)) {
// subtract from small positive, danger of UNSIGNED UNDERFLOW. Most likely, the
// inference of UNSIGNED is unreliable.
DefSignMask = FG_MASK_INCONSISTENT_SIGN;
}
}
}
}
if (OverflowOpcode) {
if ((17 == IdiomCode) && (FG_MASK_UNSIGNED == DefSignMask)) {
// An IdiomCode of 17 means not a benign overflow, but an addition that
// produces a jump table offset, which can be an addition of mixed signedness
// that produces false positives if treated as UNSIGNED. See discussion in
// SMPBasicBlock::IsBenignOverflowDEF().
DefSignMask = FG_MASK_INCONSISTENT_SIGN;
}
else if (this->IsAdditionOfLargeConstant(ConstValue, DefSignMask)) {
IgnoreOverflow = true;
IdiomCode = 11;
}
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu ",
this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth);
}
else { // must be UnderflowOpcode
// (1) Two more benign case to exclude:
// movzx eax, byte ptr [esp+37] ; get char, with misleading zero-extension
// sub eax,0x65 ; subtract 'A' to see if char is in letter range
// cmp eax,25 ; there are 26 capital letters
// ja nextcheck ; not in range of 'A' to 'Z'
// return true ; found an alphanumeric
// These operations make eax look UNSIGNED. But it is an ASCII char, and the subtraction
// will underflow whenever the char is less than 'A'. Similar checks occur for 'a'
// and the digit '0' quite often. We do not want false positives on underflow checks.
//
// (2) sub eax,[large constant value]
// We might have to follow the subtrahend operand back to its origin. A subtraction of a
// number that has a very large absolute value tends to indicate that underflow is benign
// and is being ignored.
if (!IgnoreOverflow) {
if ((DefSignMask == FG_MASK_UNSIGNED) && (this->SubtractsImmedASCII())) {
IgnoreOverflow = true;
IdiomCode = 9;
}
else if ((DefSignMask == FG_MASK_UNSIGNED) || (DefSignMask == FG_MASK_SIGNED)) {
if (this->IsSubtractionOfLargeConstant(ConstValue, DefSignMask)) {
IgnoreOverflow = true;
IdiomCode = 11;
}
}
}
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK UNDERFLOW %s %zu ",
this->address, this->SMPcmd.size, SignednessStrings[DefSignMask], DefBitWidth);
}
op_t AnnotDefOp = DefOp;
if (o_reg != DefOp.type) {
// Need to unnormalize stack memory DEFs and USEs before printing annotations.
this->MDGetUnnormalizedOp(AnnotDefOp);
}
AnnotPrintOperand(AnnotDefOp, InfoAnnotFile);
if (!IgnoreOverflow) {
// See if we made a special detection of an operation involved in a hash function, which can
// be expected to overflow benignly.
if (this->IsHashOperation()) {
IgnoreOverflow = true;
IdiomCode = 15;
}
else if (IsDataPtr(DefType)) {
// IDIOM 18 says to let C7 (buffer overflow/underflow) defenses handle the issues if it is a pointer.
IgnoreOverflow = true;
IdiomCode = 18;
}
else if (1 <= LoopList.size()) { // Limit search for address-reg-only case to loops for now.
list<size_t>::iterator LoopIter;
for (LoopIter = LoopList.begin(); LoopIter != LoopList.end(); ++LoopIter) {
size_t Counter = 0;
size_t LoopNum = (*LoopIter);
MemType = UNINIT;
this->GetBlock()->GetFunc()->ResetProcessedBlocks();
bool AddrRegOnly = this->GetBlock()->IsDefOnlyUsedAsAddressReg(this->GetAddr(), DefOp, LoopNum, Counter, MemType);
if (AddrRegOnly && (0 < Counter)) {
// Similarly, let C7 (buffer overflow/underflow) defenses handle the issues if it is only an index register.
DefType = MemType;
IgnoreOverflow = true;
IdiomCode = 18;
break; // Don't need to look at next loop
}
}
}
}
if (IgnoreOverflow) {
if (11 == IdiomCode) {
SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d CONST %u %s \n", IdiomCode, ConstValue, disasm);
}
else if (18 == IdiomCode) {
// Need a tag field telling what kind of pointer.
if (IsEqType(DefType, STACKPTR)) {
SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d STACKMEMSINK %s \n", IdiomCode, disasm);
}
else if (IsEqType(DefType, HEAPPTR)) {
SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d HEAPMEMSINK %s \n", IdiomCode, disasm);
}
else if (IsEqType(DefType, GLOBALPTR)) {
SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d GLOBALMEMSINK %s \n", IdiomCode, disasm);
}
else { // must be unrefined POINTER type.
SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d MEMORYSINK %s \n", IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, " ZZ IDIOM %d %s \n", IdiomCode, disasm);
}
}
else if (this->GetBlock()->GetFunc()->HasIntErrorCallSink(DefOp, SSANum, this->address, SinkString)) {
SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
} // end if (!(DEF is stack ptr register))
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
else {
++SuppressStackPtrOverflowCount;
}
#endif
} // end if flags reg is dead
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
else {
++SuppressLiveFlagsOverflowCount;
}
#endif
} // end cases 1-2
// Case 3: Overflow on multiplication with upper bits discarded.
if (this->MDIsMultiply()) {
// There are four overflow sub-cases for x86: (A) the multiplication result
// can go into EDX:EAX for 32x32=>64 bit multiplication; (B) the result
// can go into DX:AX for 16x16=>32 bit; (C) the result can be in AX
// for 8x8=>16 bit; (D) see below. The latter case (C) will be detected most easily
// as a truncation in a later instruction, i.e. if only AL gets stored
// later, then we check the AH bits at that time for a truncation
// error. Because our SSA numbering lumps AL, AH, AX, and EAX into
// a single canonicalized register, we would have a hard time using
// SSA-based def-use chains to determine if AH is dead.
// For the other two sub-cases, the question is whether EDX becomes dead
// starting with the DEF of EDX in the multiply instruction.
// Case (D) is where the multiply instruction discards the upper bits
// of the multiply.
// Sub-cases A&B are detected by checking if EDX is dead, and if so, then
// emitting an annotation to check for the overflow flag. The x86 sets
// overflow and carry flags on multiplication instructions based on whether
// the result carries out of the lower half of the result to the upper half.
// Sub-case D is also detected using flags, but we don't need to check whether EDX
// is dead. We just need to detect that EDX is not in the DEF set in the
// first place. We have a private member flag for that case.
CheckForOverflow = false;
if (this->AreMultiplicationBitsDiscarded()) { // Sub-case D
CheckForOverflow = true;
assert(this->RTL.GetCount() > 0);
DefOp = this->RTL.GetRT(0)->GetLeftOperand();
DefIter = this->FindDef(DefOp);
assert(DefIter != this->GetLastDef());
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
}
else {
// If the instruction were EDX:=EDX*foo, then it would be
// the multiplication bits discarded case and would not
// reach this else clause. Therefore, if we find EDX in
// the DEF set, it is holding upper result bits of the
// multiplication and we have the potential for sub-cases A&B
// but not sub-case C. So, we check to see if the DEF of EDX
// is dead.
DefOp = InitOp;
DefOp.type = o_reg;
DefOp.reg = R_dx;
DefIter = this->FindDef(DefOp);
if (DefIter != this->GetLastDef()) {
// We found DEF of EDX, so it is not AX:=AL*op8 sub-case C.
// Now, is DEF of EDX dead (i.e. no uses?)
CheckForOverflow = this->BasicBlock->IsDefDead(this->address, DefOp);
if (CheckForOverflow) {
DefHashValue = HashGlobalNameAndSSA(DefOp, DefIter->GetSSANum());
}
}
} // end if sub-case D else if sub-case A or B
if (CheckForOverflow) { // need an annotation
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++NumericAnnotationsCount3;
#endif
if (this->BasicBlock->IsLocalName(DefOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
DefWidthInfo = DefFGInfo.SizeInfo;
DefBitWidth = LargestBitWidthFromMask(DefWidthInfo);
DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
if (DefSignMask == 0) {
// If no sign info from DEF, see if we can get it from the USE regs.
DefSignMask = this->SignMaskUnionFromUseRegs();
}
// Next two statements exclude the inconsistent sign case and the no sign info known case.
DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND
DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND
uval_t ConstValue = 0;
IdiomCode = 0;
bool UnsignedAnnot = (this->MDIsUnsignedArithmetic() || DefIsUnsigned);
bool SignedAnnot = (this->MDIsSignedArithmetic() || DefIsSigned);
if (0 == DefSignMask) {
// Prepare DefSignMask for use by IsMultiplyByLargeConst(),
// as it is of no more use in case3.
if (UnsignedAnnot)
DefSignMask = FG_MASK_UNSIGNED;
else if (SignedAnnot)
DefSignMask = FG_MASK_SIGNED;
}
if (this->IsMultiplyByLargeConstant(ConstValue, DefSignMask)) {
IdiomCode = 10;
}
if (UnsignedAnnot) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW UNSIGNED %zu %s ZZ",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg]);
}
else if (SignedAnnot) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW SIGNED %zu %s ZZ",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg]);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW UNKNOWNSIGN %zu %s ZZ",
this->address, this->SMPcmd.size, DefBitWidth, RegNames[DefOp.reg]);
}
if (0 < IdiomCode) {
SMP_fprintf(InfoAnnotFile, " IDIOM %d CONST %u %s \n", IdiomCode, ConstValue, disasm);
}
else {
SMP_fprintf(InfoAnnotFile, " %s \n", disasm);
}
} // end if (CheckForOverflow)
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
else {
++LiveMultiplyBitsCount;
}
#endif
} // end of case 3
// Case 4: Signedness error on move.
// Case 5: Truncation error on move.
IdiomCode = 0;
UseOp = this->GetMoveSource();
if ((3 == this->OptType) && (o_reg == UseOp.type)) {
int TruncationIdiomCode = 0;
bool SuppressTruncation = this->IsBenignTruncation(TruncationIdiomCode);
// Possibilities for a move: reg to reg, mem to reg, or reg to mem. If we load
// from memory into a register, we cannot track signedness in memory unless it
// is a stack location. In that case, we record the signedness in the stack
// map and transfer it to the reg DEF in SMPInstr.MDSetWidthSignInfo(). That
// determines the signedness of the reg DEF and it cannot be in conflict with
// the stack memory USE. The load from stack to reg also determines width
// of the stack operand and we cannot have a truncation. So, we can restrict
// our analysis of cases 4-5 to register-source move instructions, as we
// have done in the condition above.
//
// Similarly, we cannot detect a signedness conflict if the destination is a
// memory location that is not known to be a particular stack offset location.
//
// So, we only concern ourselves with signedness errors
// when the USE operand of the move is a register, and the destination is another
// register or a stack location.
//
// We can have a truncation error and a signedness error on a single instruction, so
// we group them into common code. For example, move the lower half of a 32-bit unsigned
// into a 16-bit signed destination. Upper bits set to 1 and discarded would be a
// truncation, and setting the sign bit of the 16-bit signed destination would be a
// signedness error.
//
// NOTE: Signedness errors are different from overflow and truncation errors. We
// can have incomplete knowledge about an instruction's operands and still determine
// that truncation occurred. For example, if we do not know whether register EAX
// is signed or unsigned, we can still say that storing only AX is a truncation error
// if the upper half of EAX is a mixture of one and zero bits. If EAX is unsigned,
// we could be more specific and insist that the upper half be all zero bits; if EAX
// is signed, we could insist that the upper half of EAX be the sign-extension of AX.
// We can avoid false positives by only declaring a truncation error when the upper
// half of EAX is not all zero bits or all one bits. This approach allows a few
// potential false negatives. With signedness, if we don't know the signedness
// of one of the operands, we can only avoid false positives by doing no checks at
// all.
UseIter = this->FindUse(UseOp);
assert(UseIter != this->GetLastUse());
UseBitWidth = 8 * GetOpDataSize(UseOp);
// Now, the question is: Are we storing fewer bits than
// we were using in our computations in this DEF-USE chain?
// E.g. if we computed using 32 bits and then only store 16,
// we have potential truncation error. But if we computed
// using 16 bits all along, we have already checked for 16-bit
// overflows on arithmetic in the DU chain and there can be no
// truncation on this store.
op_t SearchOp = UseOp;
// Canonicalize sub-regs for searching DEFs and USEs.
CanonicalizeOpnd(SearchOp);
SearchOp.dtyp = dt_dword;
UseHashValue = HashGlobalNameAndSSA(SearchOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(SearchOp)) {
// Local name, find in basic block maps.
SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
SourceDefWidthInfo = SourceDefFGInfo.SizeInfo;
UseWidthInfo = UseFGInfo.SizeInfo;
SourceDefBitWidth = LargestBitWidthFromMask(SourceDefWidthInfo);
UseMaxBitWidth = LargestBitWidthFromMask(UseWidthInfo);
UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
SourceDefSignInfo = SourceDefFGInfo.SignMiscInfo;
SourceDefSignMask = (SourceDefSignInfo & FG_MASK_SIGNEDNESS_BITS);
#if 1
// If we have no signedness info at all for the UseSignMask, but
// the SourceDefSignMask has info, then we want to use the
// SourceDefSignMask as our signedness info. This is because of
// simple instruction sequences in which no signedness can be inferred
// from the use, e.g.:
//
// imul eax,ebx ; eax := eax*ebx and eax&ebx are signed
// mov [ebp-8],al ; store al on stack
//
// If we don't know the signedness of stack location [ebp-8],
// then we will end up in the S->? case below and we will emit
// a CHECK TRUNCATION UNKNOWNSIGN annotation. This discards the
// knowledge we have that EAX is signed, from the IMUL opcode.
if (0 == UseSignMask) {
UseSignMask = SourceDefSignMask;
}
#endif
// Next six statements exclude the inconsistent sign case and the no sign info known case.
UseIsSigned = (FG_MASK_SIGNED == UseSignMask); // exact, not bit-mask-AND
UseIsUnsigned = (FG_MASK_UNSIGNED == UseSignMask); // exact, not bit-mask-AND
SourceDefIsSigned = (FG_MASK_SIGNED == SourceDefSignMask); // exact, not bit-mask-AND
SourceDefIsUnsigned = (FG_MASK_UNSIGNED == SourceDefSignMask); // exact, not bit-mask-AND
UseSignMixed = (FG_MASK_INCONSISTENT_SIGN == UseSignMask); // exclude uninit sign case
SourceDefSignMixed = (FG_MASK_INCONSISTENT_SIGN == SourceDefSignMask); // exclude uninit sign case
// Not only the CHECK SIGNEDNESS annotations depend on the signedness of the
// source and destination operands. The CHECK TRUNCATION annotations come
// in SIGNED, UNSIGNED, and UNKNOWNSIGN variants, so we need to get the
// signedness of the destination operand before we proceeed.
DefOp = this->RTL.GetRT(0)->GetLeftOperand(); // RTL must be dest := rhs
op_t DestSearchOp = DefOp;
bool StackDestination = false;
// Outargs locations are reused for different function calls, so no inference of their
// signedness is valid. We maintain a flag to suppress signedness checks on writes to
// outargs locations on the stack. We make an exception for the known unsigned args to
// certain library functions.
bool OutArgsWrite = false;
bool IgnoreOutArgsWrite = false;
bool NonStackMemDestination = false;
if (o_reg == DestSearchOp.type) {
StackDestination = false;
CanonicalizeOpnd(DestSearchOp);
}
else if (!(MDIsStackAccessOpnd(DefOp, UseFP))) {
// If destination of move is not a register and is not
// a stack location, we cannot track its signedness and width.
NonStackMemDestination = true;
}
else {
StackDestination = true;
}
DefIter = this->FindDef(DestSearchOp);
DefSSANum = DefIter->GetSSANum();
if (StackDestination) {
// Fetch FG info from stack map.
bool success = this->GetBlock()->GetFunc()->MDGetFGStackLocInfo(this->address, DefOp, DefFGInfo);
assert(success);
OutArgsWrite = this->GetBlock()->GetFunc()->IsInOutgoingArgsRegion(DefOp);
IgnoreOutArgsWrite = OutArgsWrite && (!(this->IsUnsignedArg())); // ignore out args write iff not a known unsigned argument
}
else if (NonStackMemDestination) {
// We can check truncation of USE operand, but we know nothing about
// the DEF operand.
DefFGInfo.SignMiscInfo = 0;
DefFGInfo.SizeInfo = 0;
}
else {
// Fetch FG info from register FG info maps.
DefHashValue = HashGlobalNameAndSSA(DestSearchOp, DefSSANum);
if (this->BasicBlock->IsLocalName(DestSearchOp)) {
// Local name, find in basic block maps.
DefFGInfo = this->BasicBlock->GetDefFGInfo(DefHashValue);
}
else { // Global name, find in global maps.
DefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(DefHashValue);
}
}
DefSignMask = (DefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
// Next two statements exclude the inconsistent sign case and the no sign info known case.
DefIsSigned = (FG_MASK_SIGNED == DefSignMask); // exact, not bit-mask-AND
DefIsUnsigned = (FG_MASK_UNSIGNED == DefSignMask); // exact, not bit-mask-AND
// Get the Def bit width and maximum bit width for special cases.
DefWidthInfo = DefFGInfo.SizeInfo;
DefMaxBitWidth = LargestBitWidthFromMask(DefWidthInfo); // max width over all defs
DefBitWidth = 8 * GetOpDataSize(DefOp); // width of def in current instruction
PartialStore = (DefBitWidth < DefMaxBitWidth);
if (StackDestination) {
// We only do signedness checks in limited, safe analysis cases on stack writes.
SuppressSignednessCheck = this->SkipSignednessCheckOnStackWrite(DefSSANum);
if (SuppressSignednessCheck) {
IdiomCode = 7;
}
}
else {
// Check signedness on regs, but not on non-stack memory DEFs.
SuppressSignednessCheck = NonStackMemDestination;
}
// If we set the (source) DEF bit width to 0, it means we wanted to have the USEs determine
// the width. This happens on sign-extended and zero-extended loads. If we zero-extend
// a 16-bit value to 32 bits, then immediately store the lower 16 bits to a 16-bit location,
// then the upper bits cannot have any overflow info yet. But if we do 32-bit arithmetic
// on the zero-extended value, and then store the lower 16 bits, we need to check for
// truncation. So, the key is whether the value ever got used as a 32-bit value. If it
// did, check for truncation; if not, there is no need to check.
if ((SourceDefBitWidth > UseBitWidth)
|| ((SourceDefBitWidth == 0) && (UseMaxBitWidth > UseBitWidth))) {
// Original DEF (or subsequent USE) was wider than what we are storing now.
if (SourceDefBitWidth == 0) { // Convert for printing annotation.
SourceDefBitWidth = 8 * GetOpDataSize(SearchOp);
}
// OK, we need to check for possible truncation. But, how we check depends on the
// signedness combinations of the source and destination operands of the move.
// Each operand can be signed, unsigned, or of unknown sign (and we lump the
// inconsistent sign case into the unknown sign case). So, we have a set of 3x3=9
// possible combinations of signedness.
// Now we have the DefSignMask to compare to the UseSignMask. The nine possible
// combinations, and the annotations we want to emit for each, are shown below.
// S = SIGNED, U = UNSIGNED, and ? = unknown or inconsistent sign.
// S => U indicates a SIGNED being stored into an UNSIGNED, for example.
// Assume without loss of generality that register EAX is the source of
// all the move instructions, and that only subword register AX is being stored.
// We can perform all truncation and signedness checks on EAX just prior to
// the move instruction, which is cheaper than performing checks on the
// destination if the destination is in memory.
//
// U => U
// U => S
// S => U
// U => ?
// ? => U
//
// In these first five cases, EAX must be the zero-extension of AX else there is
// a truncation error. In the three cases in which the source (EAX/AX) is UNSIGNED,
// discarding upper bits that are not zero is obviously truncation. In the case
// of S => U, if the upper bits of EAX are not all zeroes, then we either have
// a large positive value of EAX that is being truncated, or EAX is negative and
// the lower bits will be misinterpreted in the unsigned destination. Finally,
// the ? => U case must be either U => U or S => U, and these two cases already
// share the demand that EAX be the zero-extension of AX. So, these five cases
// will receive the annotation: CHECK TRUNCATION UNSIGNED 32 EAX 16 AX which
// means that EAX is tested against AX to see if it is the 32-bit zero-extension
// of 16-bit reg AX.
// In the U => S case, we can have a signedness error as well as truncation. Even
// if the truncation check passes (all upper half bits of EAX are zero), the top
// bit of AX might be 1, and this will be misinterpreted as a sign bit in the
// destination. So, this case receives a second annotation: CHECK SIGNEDNESS SIGNED 16 AX.
// In the two cases that involve signedness uncertainty, there are possible signedness
// errors that we are not checking at tun-time, because we do not have enough information
// to perform the checks without generating many more false positives than true positives.
// As a result, false negatives on signedness can occur.
//
// On to more of the 9 combinations:
//
// S => S
//
// In this case, EAX must be the sign-extension of AX. Because the destination is also
// signed, nothing is lost if the sign-extension bits (all zeroes or all ones) are dropped.
// We emit a CHECK TRUNCATION SIGNED 32 EAX 16 AX annotation to test EAX == sign-extended AX.
//
// S => ?
// ? => S
// ? => ?
//
// These final three cases all involve at least one operand of unknown signedness, and no
// operands that are known to be unsigned. In each case, there are two possibilities:
// either EAX must be the sign-extension of AX, or EAX must be the zero-extension of AX.
// Because of the uncertainty that is represented by the question marks, we are not sure
// which of these two cases we are dealing with. However, rather than just give up and
// perform no run-time checks (to avoid false positives), we can still perform a run-time
// check that will catch (perhaps most) true positives while causing no false positives.
// We can insist that EAX must be EITHER the sign-extension or the zero-extension of AX.
// To be neither type of extension of AX implies that some sort of truncation is happening.
// So, we emit a CHECK TRUNCATION UNKNOWNSIGN 32 EAX 16 AX annotation, and the Strata
// instrumentation will check for either EAX == sign-extended AX or EAX == zero-extended AX
// being true. If neither is true, we raise a true positive alert. False negatives on
// signedness errors are the result of the uncertainty, but all truncations are detected
// for all nine cases.
bool HasSinkString = this->GetBlock()->GetFunc()->HasIntErrorCallSink(DestSearchOp, DefSSANum, this->address, SinkString);
if (DefIsUnsigned || UseIsUnsigned) {
// First five cases above: any UNSIGNED operand leads to CHECK TRUNCATION UNSIGNED annotation.
if (!SuppressTruncation) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION UNSIGNED %zu %s %zu %s",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp));
if (HasSinkString) {
SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++TruncationAnnotationsCount;
#endif
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION UNSIGNED %zu %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), TruncationIdiomCode, disasm);
}
if (UseIsUnsigned && DefIsSigned && !OutArgsWrite && !PartialStore) {
if (!SuppressSignednessCheck && (0 == IdiomCode)) {
unsigned short DummySignMask;
if ((0 != TruncationIdiomCode) && this->MDIsSignedLoad(DummySignMask)) {
// No truncation, extended load, so signedness check is unreliable.
IdiomCode = 20;
SuppressSignednessCheck = true;
}
}
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
}
else if (DefIsSigned && UseIsSigned) {
// S => S case above. Emit CHECK TRUNCATION SIGNED annotation.
if (!SuppressTruncation) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION SIGNED %zu %s %zu %s",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp));
if (HasSinkString) {
SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++TruncationAnnotationsCount;
#endif
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION SIGNED %zu %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), TruncationIdiomCode, disasm);
}
}
else {
// S => ?, ? => S, ? => ? cases above: CHECK TRUNCATION UNKNOWNSIGN annotation.
if (!SuppressTruncation) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION UNKNOWNSIGN %zu %s %zu %s",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp));
if (HasSinkString) {
SMP_fprintf(InfoAnnotFile, " ZZ %s %s \n", SinkString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, " ZZ %s \n", disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++TruncationAnnotationsCount;
#endif
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION UNKNOWNSIGN %zu %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, SourceDefBitWidth,
MDGetRegName(SearchOp), UseBitWidth, MDGetRegName(UseOp), TruncationIdiomCode, disasm);
}
}
#if 1
// Now check for signedness conflicts between the UseOp USEs and its DEF.
if (!OutArgsWrite && !PartialStore) { // Inferred signedness of outargs location is invalid anyway
if (!SuppressSignednessCheck && (0 == IdiomCode)) {
unsigned short DummySignMask;
if ((0 != TruncationIdiomCode) && this->MDIsSignedLoad(DummySignMask)) {
// No truncation, extended load, so signedness check is unreliable.
IdiomCode = 20;
SuppressSignednessCheck = true;
}
}
if (UseIsSigned && SourceDefIsUnsigned) {
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
else if (UseIsUnsigned && SourceDefIsSigned) {
// Currently same annotation, but might differ in the future for better forensics
// and more precise diagnostic messages.
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
else if ((!SourceDefSignMixed) && UseSignMixed) {
// DEF has consistent and known signedness, USE is inconsistent.
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, SignednessStrings[SourceDefSignMask],
UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, SignednessStrings[SourceDefSignMask],
UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
if (SuppressSignednessCheck || (0 < IdiomCode)) {
++SuppressSignednessOnTruncation;
}
#endif
#endif
} // end if truncation
else if (!IgnoreOutArgsWrite && !PartialStore) { // still need to check for signedness errors even if no truncation
if (!SuppressSignednessCheck && (0 == IdiomCode)) {
unsigned short DummySignMask;
if (this->MDIsSignedLoad(DummySignMask)) {
// No truncation, extended load, so signedness check is unreliable.
IdiomCode = 20;
SuppressSignednessCheck = true;
}
}
if (this->IsUnsignedArg()) {
DefIsUnsigned = true;
}
if (UseIsSigned && DefIsUnsigned) {
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
else if (UseIsUnsigned && DefIsSigned) {
// Currently UNSIGNED and SIGNED annotations for signedness differ only in the
// arithmetic saturation value applied as a recovery action. UNSIGNED means
// left-hand-side is UNSIGNED so saturate a negative right-hand-side up to 0,
// while SIGNED means lhs is SIGNED so saturate large rhs to 0x7fff....
// For inconsistent sign cases we default to SIGNED.
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
else if (UseIsSigned && SourceDefIsUnsigned) {
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS UNSIGNED %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
else if (UseIsUnsigned && SourceDefIsSigned) {
// Currently same annotation, but might differ in the future for better forensics
// and more precise diagnostic messages.
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), IdiomCode, disasm);
}
}
#if 0
else if ((!SourceDefSignMixed) && UseSignMixed) {
// source DEF has consistent and known signedness, source USE is inconsistent.
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK SIGNEDNESS SIGNED %u %s ZZ %s \n",
this->address, this->SMPcmd.size, UseBitWidth, MDGetRegName(UseOp), disasm);
}
#endif
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
if (!SuppressSignednessCheck || (0 == IdiomCode)) {
++SignednessWithoutTruncationCount;
}
else {
++SuppressSignednessOnTruncation;
}
#endif
} // end if truncation else check signedness
} // end of cases 4-5, (3 == OptType) checking for TRUNCATION and SIGNEDNESS errors
else if ((this->MDIsLoadEffectiveAddressInstr()) && (!(this->IsNop() || this->IsRegClearIdiom()))) {
// Case 6: Load Effective Address opcodes can do arithmetic that silently overflows.
this->MDEmitLeaOpcodeOverflowAnnotations(InfoAnnotFile, LoopList);
} // end of case 6, LoadEffectiveAddress instruction
else if (this->MDDoublesWidth()) {
// case 7: half of register is sign-extended into the full register.
// Potential truncation error if entire register was DEFed first, because
// only the lower half is used. That also makes a potential signedness
// error, because the "sign bit" that is being extended is really a data bit,
// not a sign bit.
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
DefOp = DefIter->GetOp();
assert(o_reg == DefOp.type);
SSANum = DefIter->GetSSANum();
DefHashValue = HashGlobalNameAndSSA(DefOp, SSANum);
UseIter = this->GetFirstUse();
assert(UseIter != this->GetLastUse());
UseOp = UseIter->GetOp();
assert(o_reg == UseOp.type);
SSANum = UseIter->GetSSANum();
UseHashValue = HashGlobalNameAndSSA(UseOp, SSANum);
if (this->GetBlock()->IsLocalName(DefOp)) {
DefFGInfo = this->GetBlock()->GetDefFGInfo(DefHashValue);
SourceDefFGInfo = this->GetBlock()->GetDefFGInfo(UseHashValue);
}
else {
DefFGInfo = this->GetBlock()->GetFunc()->GetDefFGInfo(DefHashValue);
SourceDefFGInfo = this->GetBlock()->GetFunc()->GetDefFGInfo(UseHashValue);
}
DefWidthInfo = DefFGInfo.SizeInfo;
DefMaxBitWidth = LargestBitWidthFromMask(DefWidthInfo); // max width over all defs
SourceDefWidthInfo = SourceDefFGInfo.SizeInfo;
SourceDefBitWidth = LargestBitWidthFromMask(SourceDefWidthInfo); // max width over all defs
// Was the USE in the width-doubling instruction originally DEFed to have more bits
// than are used in the width-doubling instruction? If so, we have truncation and
// signedness errors to check at run time.
UseBitWidth = (DefMaxBitWidth / 2); // USE should have been DEFed with this bit width
if (SourceDefBitWidth > UseBitWidth) {
// Make checks signed, because of the implications of sign extension by these opcodes.
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK TRUNCATION SIGNED %zu %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, DefMaxBitWidth,
MDGetRegName(DefOp), UseBitWidth, MDGetRegName(UseOp), disasm);
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++WidthDoublingTruncationCount;
#endif
}
} // end of case 7, doubles width inside register
else if ((BADADDR != this->CallTarget) && (!this->IsCallUsedAsJump())) {
// PEASOUP wants info on memset() calls.
string FuncName = this->GetTrimmedCalledFunctionName();
if (0 == FuncName.compare("memset")) {
op_t MemSetTarget;
size_t MemSetSize;
int StackOffset;
bool FPRelativeTarget = false; // original target before stack delta normalization
if (this->GetBlock()->AnalyzeMemSet(this->GetAddr(), MemSetTarget, MemSetSize, StackOffset, FPRelativeTarget)) {
if (0 < MemSetSize) {
// Emit annotation.
// NOTE: We want the unnormalized stack operand, so that Strata and SPRI will get
// annotations that match what is seen in the assembly language.
if (FPRelativeTarget) {
// Must be negative offset from EBP.
assert(UseFP);
StackOffset -= (int) this->GetBlock()->GetFunc()->GetFramePtrStackDelta();
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR MEMSET STACKOFFSET_EBP %d SIZE %zu ZZ %s \n",
this->address, this->SMPcmd.size, StackOffset, MemSetSize, disasm);
}
else {
// Must be non-negative offset from ESP.
StackOffset -= this->GetStackPtrOffset();
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR MEMSET STACKOFFSET_ESP %d SIZE %zu ZZ %s \n",
this->address, this->SMPcmd.size, StackOffset, MemSetSize, disasm);
}
}
}
}
} // end of memset() case
return;
} // end of SMPInstr::EmitIntegerErrorAnnotations()
#define MAX_OFFSET_STR_LEN 40
// Emit overflow annotations for lea opcodes that perform arithmetic that
// can overflow without affecting the flags.
void SMPInstr::MDEmitLeaOpcodeOverflowAnnotations(FILE *InfoAnnotFile, list<size_t> &LoopList) {
set<DefOrUse, LessDefUse>::iterator UseIter, DefIter;
size_t OpNum;
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t offset;
int SignedOffset;
bool ScaledIndexReg;
bool SourceFound = false;
bool DataPointer = false;
int SSANum;
SMPOperandType DefType, UseType;
int UseHashValue;
int IdiomCode = 0;
struct FineGrainedInfo UseFGInfo, SourceDefFGInfo;
unsigned short UseSignMask, SourceDefSignMask, BaseRegSignMask, TempSignMask;
unsigned short BaseRegWidthInfo, UseBitWidthInfo;
size_t BaseRegMaxWidth, IndexRegMaxWidth, TempMaxWidth;
char OffsetString[MAX_OFFSET_STR_LEN];
op_t TempOp, DefOp;
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & UseMacros[OpNum]) { // USE
if ((TempOp.type >= o_mem) && (TempOp.type <= o_displ)) {
MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset);
SignedOffset = (int) offset; // IDA Pro makes offset unsigned for some reason.
SourceFound = true;
break;
}
}
}
assert(SourceFound);
if (MDIsStackAccessOpnd(TempOp, this->BasicBlock->GetFunc()->UsesFramePointer())) {
// If the lea operand is something like lea ebx,[esp+4] we do not want to waste
// time checking for overflow, because stack pointers and frame pointers
// are kept within integer bounds (and if not, something big and terrible will
// happen long before integer bounds are overflowed or underflowed).
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++SuppressStackPtrOverflowCount;
#endif
return;
}
else { // Check for the benign overflow cases we want to ignore.
DefIter = this->GetFirstNonFlagsDef();
assert(DefIter != this->GetLastDef());
DefOp = DefIter->GetOp();
SSANum = DefIter->GetSSANum();
DefType = DefIter->GetType();
DataPointer = IsDataPtr(DefType);
if (this->IsHashOperation()) {
IdiomCode = 15;
}
else if (this->GetBlock()->IsBenignOverflowDEF(DefOp, SSANum, this->GetAddr(), this->MDIsUnderflowingOpcode(), IdiomCode)) {
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++BenignOverflowDefCount;
#endif
;
}
}
if (0 == IdiomCode) {
// Any overflow or underflow producing a PTROFFSET type should be suppressed.
for (DefIter = this->GetFirstDef(); DefIter != this->GetLastDef(); ++DefIter) {
DefType = DefIter->GetType();
if (IsEqType(DefType, PTROFFSET)) {
IdiomCode = 19;
}
else if (IsEqType(DefType, NEGATEDPTR)) {
IdiomCode = 13;
}
}
}
if (0 == IdiomCode) {
// Any overflow or underflow using a PTROFFSET type should be suppressed.
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
UseType = UseIter->GetType();
if (IsEqType(UseType, PTROFFSET)) {
IdiomCode = 19;
}
else if (IsEqType(UseType, NEGATEDPTR)) {
IdiomCode = 13;
}
}
}
char *disasm = DisAsmText.GetDisAsm(this->GetAddr());
ScaledIndexReg = (0 < ScaleFactor);
if (0 != SignedOffset) {
(void) SMP_snprintf(OffsetString, MAX_OFFSET_STR_LEN-2, "%d", SignedOffset);
}
// Build up the computational string one operation at a time and emit
// CHECK OVERFLOW annotations after each step.
string CurrString("");
const char *ScaleStrings[4] = {"", "*2", "*4", "*8"};
string PtrString("");
if (IdiomCode == 0) {
if (!DataPointer && (1 == LoopList.size())) { // Start with simple case of blocks only present in one loop
SMPOperandType MemType = UNINIT;
size_t Counter = 0;
size_t LoopNum = LoopList.front();
this->GetBlock()->GetFunc()->ResetProcessedBlocks();
bool AddrRegOnly = this->GetBlock()->IsDefOnlyUsedAsAddressReg(this->GetAddr(), DefOp, LoopNum, Counter, MemType);
if (AddrRegOnly && (0 < Counter)) {
// Similarly, let C7 (buffer overflow/underflow) defenses handle the issues if it is only an index register.
DefType = MemType;
DataPointer = true;
}
}
if (DataPointer) {
if (IsEqType(STACKPTR, DefType))
PtrString = "STACKMEMSINK";
else if (IsEqType(HEAPPTR, DefType))
PtrString = "HEAPMEMSINK";
else if (IsEqType(GLOBALPTR, DefType))
PtrString = "GLOBALMEMSINK";
else
PtrString = "MEMORYSINK";
IdiomCode = 18;
}
}
op_t RegOp;
RegOp.type = o_reg;
// Gather signedness info about BaseReg, if any, that will be used in multiple cases below.
if (R_none != BaseReg) {
RegOp.reg = MDCanonicalizeSubReg(BaseReg);
RegOp.dtyp = dt_dword;
UseIter = this->FindUse(RegOp);
assert(UseIter != this->GetLastUse());
UseHashValue = HashGlobalNameAndSSA(RegOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(RegOp)) {
// Local name, find in basic block maps.
SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
SourceDefSignMask = (SourceDefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
if (UseSignMask == 0) {
// Get signedness info wherever it is available.
UseSignMask = SourceDefSignMask;
}
BaseRegSignMask = UseSignMask;
if ((17 == IdiomCode) && (FG_MASK_UNSIGNED == BaseRegSignMask)) {
// An IdiomCode of 17 means not a benign overflow, but an addition that
// produces a jump table offset, which can be an addition of mixed signedness
// that produces false positives if treated as UNSIGNED. See discussion in
// SMPBasicBlock::IsBenignOverflowDEF().
BaseRegSignMask = FG_MASK_INCONSISTENT_SIGN;
}
BaseRegWidthInfo = (UseFGInfo.SizeInfo & FG_MASK_BITWIDTH_FIELDS);
if (0 == BaseRegWidthInfo) {
BaseRegWidthInfo = (SourceDefFGInfo.SizeInfo & FG_MASK_BITWIDTH_FIELDS);
}
BaseRegMaxWidth = LargestBitWidthFromMask(BaseRegWidthInfo);
}
else {
BaseRegMaxWidth = 0;
BaseRegSignMask = 0;
}
if (R_none != IndexReg) {
// Get signedness info for IndexReg.
RegOp.reg = MDCanonicalizeSubReg(IndexReg);
RegOp.dtyp = dt_dword;
UseIter = this->FindUse(RegOp);
assert(UseIter != this->GetLastUse());
UseHashValue = HashGlobalNameAndSSA(RegOp, UseIter->GetSSANum());
if (this->BasicBlock->IsLocalName(RegOp)) {
// Local name, find in basic block maps.
SourceDefFGInfo = this->BasicBlock->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetUseFGInfo(UseHashValue);
}
else { // Global name, find in global maps.
SourceDefFGInfo = this->BasicBlock->GetFunc()->GetDefFGInfo(UseHashValue);
UseFGInfo = this->BasicBlock->GetFunc()->GetUseFGInfo(UseHashValue);
}
UseSignMask = (UseFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
SourceDefSignMask = (SourceDefFGInfo.SignMiscInfo & FG_MASK_SIGNEDNESS_BITS);
if (UseSignMask == 0) {
// Get signedness info wherever it is available.
UseSignMask = SourceDefSignMask;
}
if ((17 == IdiomCode) && (FG_MASK_UNSIGNED == UseSignMask)) {
// An IdiomCode of 17 means not a benign overflow, but an addition that
// produces a jump table offset, which can be an addition of mixed signedness
// that produces false positives if treated as UNSIGNED. See discussion in
// SMPBasicBlock::IsBenignOverflowDEF().
UseSignMask = FG_MASK_INCONSISTENT_SIGN;
IdiomCode = 0; // reset; not a benign code idiom
}
UseBitWidthInfo = (UseFGInfo.SizeInfo & FG_MASK_BITWIDTH_FIELDS);
IndexRegMaxWidth = LargestBitWidthFromMask(UseBitWidthInfo);
TempSignMask = (BaseRegSignMask | UseSignMask);
TempMaxWidth = (BaseRegMaxWidth >= IndexRegMaxWidth) ? BaseRegMaxWidth : IndexRegMaxWidth;
if (ScaledIndexReg) {
assert((ScaleFactor >= 1) && (ScaleFactor <= 3));
assert((IndexReg >= R_ax) && (IndexReg <= R_bh));
CurrString += RegNames[IndexReg];
CurrString += ScaleStrings[ScaleFactor];
if (IdiomCode > 0) {
if (IdiomCode == 18) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[UseSignMask],
IndexRegMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[UseSignMask],
IndexRegMaxWidth, CurrString.c_str(), IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[UseSignMask],
IndexRegMaxWidth, CurrString.c_str(), disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++LeaInstOverflowCount;
#endif
if (R_none != BaseReg) {
// Have BaseReg+IndexReg*ScaleFactor
string TempStr(CurrString);
CurrString.clear();
CurrString += RegNames[BaseReg];
CurrString += "+";
CurrString += TempStr;
if (IdiomCode > 0) {
if (IdiomCode == 18) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++LeaInstOverflowCount;
#endif
}
if (0 != SignedOffset) {
// We have [ BaseReg + ] IndexReg*ScaleFactor+offset [BaseReg or not]
// CurrString has everything through the end of the scalefactor in either case.
#if 0 // easier for instrumentation to always use ADD, even for negative offsets
if (0 < SignedOffset)
#endif
CurrString += "+";
CurrString += OffsetString;
if (IdiomCode > 0) {
if (IdiomCode == 18) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++LeaInstOverflowCount;
#endif
}
}
else if (R_none != BaseReg) {
// We have BaseReg+IndexReg, unscaled.
CurrString += RegNames[BaseReg];
CurrString += "+";
CurrString += RegNames[IndexReg];
if (IdiomCode > 0) {
if (IdiomCode == 18) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++LeaInstOverflowCount;
#endif
if (0 != SignedOffset) {
// We have BaseReg + IndexReg + offset
#if 0 // easier for instrumentation to always use ADD, even for negative offsets
if (0 < SignedOffset)
#endif
CurrString += "+";
CurrString += OffsetString;
if (IdiomCode > 0) {
if (IdiomCode == 18) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++LeaInstOverflowCount;
#endif
}
}
else if (0 != SignedOffset) {
// We have just IndexReg+offset.
CurrString += RegNames[IndexReg];
#if 0 // easier for instrumentation to always use ADD, even for negative offsets
if (0 < SignedOffset)
#endif
CurrString += "+";
CurrString += OffsetString;
if (IdiomCode > 0) {
if (IdiomCode == 18) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[TempSignMask],
TempMaxWidth, CurrString.c_str(), disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++LeaInstOverflowCount;
#endif
}
else {
// Just IndexReg, no BaseReg or offset, so nothing to do.
;
}
}
else if ((R_none != BaseReg) && (SignedOffset != 0)) {
// No index reg, scaled or otherwise. Just BaseReg+offset
CurrString += RegNames[BaseReg];
if (17 == IdiomCode) {
IdiomCode = 0; // reset; not a benign code idiom
}
#if 0 // easier for instrumentation to always use ADD, even for negative offsets
if (0 < SignedOffset)
#endif
CurrString += "+";
CurrString += OffsetString;
if (IdiomCode > 0) {
if (IdiomCode == 18) {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[BaseRegSignMask],
BaseRegMaxWidth, CurrString.c_str(), IdiomCode, PtrString.c_str(), disasm);
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ IDIOM %d %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[BaseRegSignMask],
BaseRegMaxWidth, CurrString.c_str(), IdiomCode, disasm);
}
}
else {
SMP_fprintf(InfoAnnotFile, "%10x %6d INSTR CHECK OVERFLOW %s %zu %s ZZ %s \n",
this->address, this->SMPcmd.size, LeaSignednessStrings[BaseRegSignMask],
BaseRegMaxWidth, CurrString.c_str(), disasm);
}
#if SMP_MEASURE_NUMERIC_ANNOTATIONS
++LeaInstOverflowCount;
#endif
}
else {
// Either just a BaseReg, or just an offset. Nothing to do.
;
}
return;
} // end of SMPInstr::MDEmitLeaOpcodeOverflowAnnotations()
// Go through the PUSH RTL and get the operand pushed.
op_t SMPInstr::GetPushedOpnd(void) {
op_t VoidOp = InitOp;
if (NN_push == this->SMPcmd.itype) {
for (size_t OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & UseMacros[OpNum]) { // USE
return TempOp;
}
}
SMP_msg("ERROR: Could not find PUSH operand at %x %s\n", this->address,
DisAsmText.GetDisAsm(this->GetAddr()));
return VoidOp;
}
else {
return VoidOp;
}
} // end of SMPInstr::GetPushedOpnd()
// Get the immediate value used in the instruction. Return zero
// if no immediate was used.
int SMPInstr::MDGetImmedUse(void) {
int ImmedVal = 0;
set<DefOrUse, LessDefUse>::iterator CurrUse;
for (CurrUse = this->GetFirstUse(); CurrUse != this->GetLastUse(); ++CurrUse) {
op_t UseOp = CurrUse->GetOp();
if (o_imm == UseOp.type) {
ImmedVal = (int) UseOp.value;
break;
}
}
return ImmedVal;
} // end of SMPInstr::MDGetImmedUse()
// Get funcname from call inst and remove "." and "_" prefices.
// Asserts if this is not a call instruction.
string SMPInstr::GetTrimmedCalledFunctionName(void) {
SMPitype DFType = this->GetDataFlowType();
assert((CALL == DFType) || (INDIR_CALL == DFType) || this->IsTailCall());
ea_t FuncAddr = this->CallTarget;
char IDA_func_name[MAXSTR];
size_t SkipCount;
char *TempFuncName;
if (CALL == DFType) {
// We should have a good call target for direct calls.
assert(BADADDR != FuncAddr);
(void) get_func_name(FuncAddr, IDA_func_name, (size_t)(MAXSTR - 1));
SkipCount = strspn(IDA_func_name, "._");
TempFuncName = &(IDA_func_name[SkipCount]);
string FuncName(TempFuncName);
return FuncName;
}
else { // INDIR_CALL
// We might have a resolved call target for indirect calls.
if (BADADDR != FuncAddr) {
// We have a resolved address for the indirect call.
(void) get_func_name(FuncAddr, IDA_func_name, (size_t)(MAXSTR - 1));
SkipCount = strspn(IDA_func_name, "._");
TempFuncName = &(IDA_func_name[SkipCount]);
string IndirFuncName(TempFuncName);
return IndirFuncName;
}
else { // INDIR_CALL, no resolved target.
string DummyFuncName("ZST_NEVER_MATCH_THIS_FUNC_NAME");
return DummyFuncName;
}
}
} // end of SMPInstr::GetTrimmedCalledFunctionName()
// Build the RTL for an instruction with a unary opcode
bool SMPInstr::BuildUnaryRTL(SMPoperator UnaryOp) {
size_t OpNum;
bool DestFound = false;
bool WidthDoubler = this->MDDoublesWidth();
SMPRegTransfer *TempRT = NULL;
op_t VoidOp = InitOp;
op_t FPRegOp = InitOp;
FPRegOp.type = o_fpreg; // floating point register stack
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
// Handle special cases first
if (SMP_UNARY_FLOATING_ARITHMETIC == UnaryOp) {
// Use of the floating register stack top is implicit
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(FPRegOp);
TempRT->SetOperator(SMP_ASSIGN);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
RightRT->SetLeftOperand(FPRegOp);
RightRT->SetOperator(UnaryOp);
RightRT->SetRightOperand(VoidOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
}
else if ((NN_clc == this->SMPcmd.itype) || (NN_cld == this->SMPcmd.itype)
|| (NN_cmc == this->SMPcmd.itype) || (NN_stc == this->SMPcmd.itype)
|| (NN_std == this->SMPcmd.itype)) {
// Flags register is implicit destination.
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(FlagsOp);
TempRT->SetOperator(SMP_ASSIGN);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
if (NN_cmc == this->SMPcmd.itype) { // complement carry flag USEs old carry flag
RightRT->SetLeftOperand(FlagsOp);
RightRT->SetOperator(SMP_BITWISE_NOT);
}
else {
RightRT->SetLeftOperand(VoidOp);
RightRT->SetOperator(UnaryOp);
}
RightRT->SetRightOperand(VoidOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
}
for (OpNum = 0; !DestFound && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
op_t LeftOp;
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
LeftOp = TempOp;
if (WidthDoubler) {
// Opcodes that sign-extend a byte to a word, or a word to a dword,
// have only one operand. It is implicit, and it is the shorter USE.
// That means the DEF op will have the same width as the USE op, e.g. if
// we are sign-extending AX to EAX, the USE op and DEF op will both be AX
// without a special fix. We fix this problem with the DEF operand now.
if (LeftOp.dtyp == dt_byte) {
LeftOp.dtyp = dt_word;
LeftOp.reg = MDCanonicalizeSubReg(LeftOp.reg);
}
else if (LeftOp.dtyp == dt_word) {
LeftOp.dtyp = dt_dword;
LeftOp.reg = MDCanonicalizeSubReg(LeftOp.reg);
}
else if (LeftOp.dtyp == dt_dword) {
LeftOp.dtyp = dt_qword;
}
else {
SMP_msg("ERROR: Instruction operand %zu not 1,2, or 4 bytes at %x dtyp: %d\n",
OpNum, this->address, LeftOp.dtyp);
}
}
TempRT->SetLeftOperand(LeftOp);
TempRT->SetOperator(SMP_ASSIGN);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(UnaryOp);
RightRT->SetRightOperand(VoidOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
}
}
} // end for (OpNum = 0; ...)
#if SMP_DEBUG_BUILD_RTL
if (!DestFound) {
SMP_msg("ERROR: Could not find unary operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
#endif
return DestFound;
} // end of SMPInstr::BuildUnaryRTL()
#if 0
// Cleaner, no special-case version of BuildUnary2OpndRTL()
// Build the RTL for an instruction of the form dest := unary_operator(src), with src != dest
bool SMPInstr::BuildUnaryTwoOperandRTL(SMPoperator UnaryOp) {
size_t OpNum;
bool DestFound = false;
bool SrcFound = false;
op_t DestOp, SrcOp;
SMPRegTransfer *TempRT = NULL;
op_t VoidOp = InitOp;
for (OpNum = 0; (!(DestFound && SourceFound)) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
DestOp = TempOp;
}
}
else { // not dest, see if valid source
if (MDKnownOperandType(TempOp)) {
SourceFound = true;
}
}
} // end for (OpNum = 0; ...)
if (DestFound && SourceFound) {
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(DestOp);
TempRT->SetOperator(SMP_ASSIGN);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
RightRT->SetLeftOperand(SrcOp);
RightRT->SetOperator(UnaryOp);
RightRT->SetRightOperand(VoidOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
}
#if SMP_DEBUG_BUILD_RTL
else {
SMP_msg("ERROR: Could not find unary operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
#endif
return DestFound;
} // end of SMPInstr::BuildUnaryTwoOperandRTL()
#endif
// Build the RTL for an instruction with a binary arithmetic opcode
bool SMPInstr::BuildBinaryRTL(SMPoperator BinaryOp, bool HiddenFPStackOp) {
size_t OpNum;
int opcode = this->SMPcmd.itype;
bool DestFound = false;
bool SourceFound = false;
bool MemSrc = this->HasSourceMemoryOperand();
bool MemDest = this->HasDestMemoryOperand();
// Work around IDA pro error; they assumed that the pcmpeq/ne/gt/ge/lt/le
// families of instructions were just compares, so they do not tag
// either operand as a DEF. Actually, the first operand has byte or
// word or dword fields set to all 1's or all 0's based on the result
// of the comparison.
bool SrcIsReallyDest = ((SMP_COMPARE_EQ_AND_SET <= BinaryOp)
&& (SMP_COMPARE_LE_AND_SET >= BinaryOp));
bool StackPointerModification = false; // SP := SP SMP_BITWISE_AND operand
bool NeedsGuard = ((NN_arpl == opcode) || (NN_bound == opcode));
bool BuildOnlySignalRT = (NN_bound == opcode);
SMPRegTransfer *TempRT = NULL;
SMPRegTransfer *RightRT = new SMPRegTransfer;
SMPGuard *Guard1 = NULL;
RightRT->SetParentInst(this);
op_t VoidOp = InitOp;
op_t FPRegOp = InitOp;
FPRegOp.type = o_fpreg; // floating point register stack
// Handle special cases first
if (NeedsGuard) {
// Need a guard in the RTL.
Guard1 = new SMPGuard;
Guard1->SetOperator(SMP_U_COMPARE);
}
else if (HiddenFPStackOp) {
// Use of the floating register stack top is implicit
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(FPRegOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(FPRegOp);
RightRT->SetOperator(BinaryOp);
RightRT->SetRightOperand(VoidOp);
TempRT->SetRightTree(RightRT);
}
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if ((this->features & DefMacros[OpNum])
|| (SrcIsReallyDest && (0 == OpNum))) { // DEF
if (!DestFound && MDKnownOperandType(TempOp)) {
// See comments just below for floating point sources. FP stores
// are analogous to FP loads.
if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
if (!BuildOnlySignalRT) {
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
}
if (this->IsRegClearIdiom()) {
op_t ImmOp = InitOp;
ImmOp.type = o_imm;
ImmOp.value = 0;
TempRT->SetRightOperand(ImmOp);
SourceFound = true; // cause loop exit
}
else {
if (!BuildOnlySignalRT) {
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
if (TempOp.is_reg(MD_STACK_POINTER_REG) && (SMP_BITWISE_AND == BinaryOp)) {
StackPointerModification = true; // searching for SP := SP AND immediate
}
}
if (NeedsGuard) {
Guard1->SetLeftOperand(TempOp);
TempRT->SetGuard(Guard1);
}
}
}
else {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("WARNING: Skipping DEF operand: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else if (DestFound && (!HiddenFPStackOp)) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found two DEF operands: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (!SourceFound && MDKnownOperandType(TempOp)) {
// If this is a floating point instruction with the fpregs listed as
// a USE and a memory operand also listed as a USE, then we want to
// ignore the irrelevant USE of the fpreg stack.
// Note that MemDest AND MemSrc means something like add mem,reg is being
// processed, where the memory operand is both DEF and USE.
if (!MemSrc || MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
SourceFound = true;
if (!BuildOnlySignalRT) {
RightRT->SetRightOperand(TempOp);
if (StackPointerModification && (o_imm == TempOp.type)) {
this->SetStackAlignmentInst();
}
}
}
else {
SMP_msg("ERROR: Operand not processed as USE: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
if (NeedsGuard) {
Guard1->SetRightOperand(TempOp);
}
}
if (!(this->features & UseMacros[OpNum])) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: Operand neither DEF nor USE: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
} // end if DEF ... else ...
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound) {
assert(NULL != RightRT);
if (DestFound && (NULL != TempRT))
delete TempRT;
else
delete RightRT;
#if SMP_DEBUG_BUILD_RTL
if (!DestFound) {
SMP_msg("ERROR: Could not find binary DEF operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
}
else {
SMP_msg("ERROR: Could not find binary operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
#endif
}
else {
if (BuildOnlySignalRT) {
TempRT->SetLeftOperand(VoidOp);
TempRT->SetRightOperand(VoidOp);
TempRT->SetOperator(SMP_SIGNAL);
delete RightRT;
}
this->RTL.push_back(TempRT);
// The "p" at the end of the opcode indicates that the floating point
// register stack gets popped.
if ((NN_fstp == opcode) || (NN_fbstp == opcode) || (NN_fistp == opcode)) {
this->RTL.ExtraKills.push_back(FPRegOp);
}
}
return (DestFound && SourceFound);
} // end of SMPInstr::BuildBinaryRTL()
// Build the RTL for a signal/exception raised based on a guarded comparison of operands
bool SMPInstr::BuildGuardedSignalRTL(SMPoperator SignalOp) {
size_t OpNum;
bool Source1Found = false;
bool Source2Found = false;
SMPRegTransfer *TempRT = NULL;
SMPGuard *Guard1 = new SMPGuard;
op_t VoidOp = InitOp;
Guard1->SetOperator(SMP_U_COMPARE);
for (OpNum = 0; !(Source1Found && Source2Found) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("WARNING: Skipping DEF operand: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (!Source1Found && MDKnownOperandType(TempOp)) {
Source1Found = true;
Guard1->SetLeftOperand(TempOp);
}
else if ((!Source2Found) && MDKnownOperandType(TempOp)) {
Source2Found = true;
Guard1->SetRightOperand(TempOp);
}
if (!(this->features & UseMacros[OpNum])) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: Operand neither DEF nor USE: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
} // end if DEF ... else ...
} // end for (OpNum = 0; ...)
if (!Source1Found || !Source2Found) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
if (!Source1Found) {
SMP_msg("ERROR: Could not find first USE operand for BOUND at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
}
else {
SMP_msg("ERROR: Could not find second USE operand for BOUND at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
}
this->PrintOperands();
#endif
delete Guard1;
}
else {
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(VoidOp);
TempRT->SetRightOperand(VoidOp);
TempRT->SetOperator(SignalOp);
TempRT->SetGuard(Guard1);
this->RTL.push_back(TempRT);
}
return (Source1Found && Source2Found);
} // end of SMPInstr::BuildGuardedSignalRTL()
// Build the RTL for an instruction with a binary arithmetic opcode plus an operator to apply to
// the two source-only operands, one of which is an immediate value.
bool SMPInstr::BuildBinaryPlusImmedRTL(SMPoperator BinaryOp, SMPoperator ImmedOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
bool ImmedFound = false;
bool MemSrc = this->HasSourceMemoryOperand();
bool MemDest = this->HasDestMemoryOperand();
SMPRegTransfer *TempRT = NULL;
SMPRegTransfer *RightRT = new SMPRegTransfer;
SMPRegTransfer *ImmedRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
ImmedRT->SetParentInst(this);
for (OpNum = 0; !(DestFound && SourceFound && ImmedFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (!DestFound && MDKnownOperandType(TempOp)) {
if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
}
else {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("WARNING: Skipping DEF operand: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else if (DestFound) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found two DEF operands: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (!SourceFound && MDKnownOperandType(TempOp)) {
if (!MemSrc || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
SourceFound = true;
ImmedRT->SetLeftOperand(TempOp);
}
}
else if (!ImmedFound && (o_imm == TempOp.type)) {
ImmedFound = true;
ImmedRT->SetRightOperand(TempOp);
ImmedRT->SetOperator(ImmedOp);
RightRT->SetRightTree(ImmedRT);
}
if (!(this->features & UseMacros[OpNum])) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: Operand neither DEF nor USE: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
} // end if DEF ... else ...
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound || !ImmedFound) {
assert(NULL != RightRT);
if (DestFound && (NULL != TempRT))
delete TempRT;
else {
delete RightRT;
if (!ImmedFound)
delete ImmedRT;
}
#if SMP_DEBUG_BUILD_RTL
if (!DestFound) {
SMP_msg("ERROR: Could not find binary DEF operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
}
else if (!ImmedFound) {
SMP_msg("ERROR: Could not find immediate operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
else {
SMP_msg("ERROR: Could not find binary USE operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
#endif
}
else {
this->RTL.push_back(TempRT);
}
return (DestFound && SourceFound && ImmedFound);
} // end of SMPInstr::BuildBinaryPlusImmedRTL()
// Build the RTL for an instruction with a binary arithmetic opcode, ignoring a third operand
// which is an immediate value which is a control word for packed operations (e.g. SSE, XMM).
bool SMPInstr::BuildBinaryIgnoreImmedRTL(SMPoperator BinaryOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
bool ImmedFound = false;
bool MemSrc = this->HasSourceMemoryOperand();
bool MemDest = this->HasDestMemoryOperand();
bool ECXDest = ((NN_pcmpestri == this->SMPcmd.itype) || (NN_pcmpistri == this->SMPcmd.itype));
bool XMM0Dest = ((NN_pcmpestrm == this->SMPcmd.itype) || (NN_pcmpistrm == this->SMPcmd.itype));
bool SrcIsReallyDest = (ECXDest || XMM0Dest); // Dest is implicit, so ASM only has source operand
SMPRegTransfer *TempRT = NULL;
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
for (OpNum = 0; !(DestFound && SourceFound && ImmedFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if ((this->features & DefMacros[OpNum])
|| (SrcIsReallyDest && (0 == OpNum))) { // DEF
if (!DestFound && MDKnownOperandType(TempOp)) {
if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
if (ECXDest) {
op_t ECXOp = InitOp;
ECXOp.type = o_reg;
ECXOp.reg = R_cx;
ECXOp.dtyp = dt_dword;
TempRT->SetLeftOperand(ECXOp);
}
else if (XMM0Dest) {
op_t XMMOp = InitOp;
XMMOp.type = o_reg;
XMMOp.reg = R_xmm0;
XMMOp.dtyp = dt_byte16;
TempRT->SetLeftOperand(XMMOp);
}
else {
TempRT->SetLeftOperand(TempOp);
}
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
}
else {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("WARNING: Skipping DEF operand: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else if (DestFound) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found two DEF operands: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (!SourceFound && MDKnownOperandType(TempOp)) {
if (!MemSrc || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
SourceFound = true;
RightRT->SetRightOperand(TempOp);
}
}
else if (!ImmedFound && (o_imm == TempOp.type)) {
ImmedFound = true;
}
if (!(this->features & UseMacros[OpNum])) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: Operand neither DEF nor USE: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
} // end if DEF ... else ...
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound || !ImmedFound) {
assert(NULL != RightRT);
if (DestFound && (NULL != TempRT))
delete TempRT;
else
delete RightRT;
#if SMP_DEBUG_BUILD_RTL
if (!DestFound) {
SMP_msg("ERROR: Could not find binary DEF operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
}
else if (!ImmedFound) {
SMP_msg("ERROR: Could not find immediate operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
else {
SMP_msg("ERROR: Could not find binary USE operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
#endif
}
else {
this->RTL.push_back(TempRT);
}
return (DestFound && SourceFound && ImmedFound);
} // end of SMPInstr::BuildBinaryIgnoreImmedRTL()
// Build the RTL for a load-effective-address instruction.
bool SMPInstr::BuildLeaRTL(void) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
op_t DefOp = InitOp;
op_t UseOp = InitOp;
SMPRegTransfer *AssignRT = NULL;
int BaseReg;
int IndexReg;
ushort ScaleFactor;
ea_t offset;
bool ScaledIndexReg;
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
DefOp = TempOp;
DestFound = true;
assert(o_reg == DefOp.type);
}
else { // USE
if (!SourceFound && MDKnownOperandType(TempOp)) {
if ((TempOp.type >= o_mem) && (TempOp.type <= o_displ)) {
SourceFound = true;
UseOp = TempOp;
MDExtractAddressFields(TempOp, BaseReg, IndexReg, ScaleFactor, offset);
}
else {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("WARNING: Skipping USE operand: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
if (!(this->features & UseMacros[OpNum])) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: Operand neither DEF nor USE: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
} // end if DEF ... else ...
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound) {
#if SMP_DEBUG_BUILD_RTL
if (!DestFound) {
SMP_msg("ERROR: Could not find lea DEF operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
}
else {
SMP_msg("ERROR: Could not find lea USE operand at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
#endif
}
else { // Ready to build the RTL
// We build the RTL down to the right, in reverse order, with any multiplication
// of the index register by a scale factor at the bottom of the RTL tree.
// Note that almost any combination of BaseReg, IndexReg, and offset can be present
// or absent.
AssignRT = new SMPRegTransfer;
AssignRT->SetParentInst(this);
AssignRT->SetLeftOperand(DefOp);
AssignRT->SetOperator(SMP_ASSIGN);
ScaledIndexReg = ((ScaleFactor > 0) && (IndexReg != R_none));
op_t BaseOp = InitOp, IndexOp = InitOp, OffsetOp = InitOp, ScaleOp = InitOp;
BaseOp.type = o_reg;
BaseOp.reg = (ushort) BaseReg;
IndexOp.type = o_reg;
IndexOp.reg = (ushort) IndexReg;
OffsetOp.type = o_imm;
OffsetOp.value = (uval_t) offset;
ScaleOp.type = o_imm;
ScaleOp.value = (uval_t) ScaleFactor;
if (ScaledIndexReg) {
// First, build the subtree to scale the IndexReg.
SMPRegTransfer *MultRT = new SMPRegTransfer;
MultRT->SetParentInst(this);
MultRT->SetLeftOperand(IndexOp);
MultRT->SetOperator(SMP_U_LEFT_SHIFT);
MultRT->SetRightOperand(ScaleOp);
// Now, case on the possibilities for existence of the other address fields.
if (0 != offset) {
// Add the offset to the scaled index subtree.
SMPRegTransfer *AddOffRT = new SMPRegTransfer;
AddOffRT->SetParentInst(this);
AddOffRT->SetLeftOperand(OffsetOp);
AddOffRT->SetOperator(SMP_ADD);
AddOffRT->SetRightTree(MultRT);
// Add a BaseReg, if any.
if (R_none != BaseReg) {
SMPRegTransfer *AddBaseRT = new SMPRegTransfer;
AddBaseRT->SetParentInst(this);
AddBaseRT->SetLeftOperand(BaseOp);
AddBaseRT->SetOperator(SMP_ADD);
AddBaseRT->SetRightTree(AddOffRT);
// Link into assignment root tree.
AssignRT->SetRightTree(AddBaseRT);
}
else { // no BaseReg
AssignRT->SetRightTree(AddOffRT);
}
} // end if nonzero offset
else { // no offset to add
// Add a BaseReg, if any.
if (R_none != BaseReg) {
SMPRegTransfer *AddBaseRT = new SMPRegTransfer;
AddBaseRT->SetParentInst(this);
AddBaseRT->SetLeftOperand(BaseOp);
AddBaseRT->SetOperator(SMP_ADD);
AddBaseRT->SetRightTree(MultRT);
// Link into assignment root tree.
AssignRT->SetRightTree(AddBaseRT);
}
else { // no BaseReg
AssignRT->SetRightTree(MultRT);
}
}
} // end if ScaleIndexReg
else { // no scaled index register
if (0 != offset) {
if (R_none != IndexReg) {
SMPRegTransfer *AddOffRT = new SMPRegTransfer;
AddOffRT->SetParentInst(this);
AddOffRT->SetLeftOperand(OffsetOp);
AddOffRT->SetOperator(SMP_ADD);
AddOffRT->SetRightOperand(IndexOp);
// Add BaseReg, if any.
if (R_none != BaseReg) {
SMPRegTransfer *AddBaseRT = new SMPRegTransfer;
AddBaseRT->SetParentInst(this);
AddBaseRT->SetLeftOperand(BaseOp);
AddBaseRT->SetOperator(SMP_ADD);
AddBaseRT->SetRightTree(AddOffRT);
// Link into assignment root tree.
AssignRT->SetRightTree(AddBaseRT);
}
else { // no BaseReg
AssignRT->SetRightTree(AddOffRT);
}
} // end if valid IndexReg
else { // no IndexReg
// Add BaseReg, if any.
if (R_none != BaseReg) {
SMPRegTransfer *AddBaseRT = new SMPRegTransfer;
AddBaseRT->SetParentInst(this);
AddBaseRT->SetLeftOperand(BaseOp);
AddBaseRT->SetOperator(SMP_ADD);
AddBaseRT->SetRightOperand(OffsetOp);
// Link into assignment root tree.
AssignRT->SetRightTree(AddBaseRT);
}
else { // no BaseReg, no IndexReg, just offset?
if (UseOp.type != o_mem) {
SMP_msg("WARNING: lea used as move at %x for %s\n", this->address,
DisAsmText.GetDisAsm(this->GetAddr()));
}
AssignRT->SetRightOperand(OffsetOp);
}
}
} // end if nonzero offset
else { // no offset
if ((R_none == BaseReg) || (R_none == IndexReg)) {
SMP_msg("WARNING: lea used as move at %x for %s\n", this->address,
DisAsmText.GetDisAsm(this->GetAddr()));
if (R_none != BaseReg) {
AssignRT->SetRightOperand(BaseOp);
}
else {
if (R_none == IndexReg) { // Should be RegClearIdiom in this case, no longer Lea RTL
assert(this->IsRegClearIdiom());
op_t ImmOp = InitOp;
ImmOp.type = o_imm;
ImmOp.value = 0;
AssignRT->SetRightOperand(ImmOp);
}
else {
AssignRT->SetRightOperand(IndexOp);
}
}
}
else { // we have a BaseReg and an IndexReg, unscaled, no offset
SMPRegTransfer *AddBaseRT = new SMPRegTransfer;
AddBaseRT->SetParentInst(this);
AddBaseRT->SetLeftOperand(BaseOp);
AddBaseRT->SetOperator(SMP_ADD);
AddBaseRT->SetRightOperand(IndexOp);
// Link into assignment root tree.
AssignRT->SetRightTree(AddBaseRT);
}
} // end if nonzero offset ... else ...
} // end if (ScaledIndexReg) ... else ...
this->RTL.push_back(AssignRT);
}
return (DestFound && SourceFound);
} // end of SMPInstr::BuildLeaRTL()
// Build the RTL for an double-word shift instruction
bool SMPInstr::BuildDoubleShiftRTL(SMPoperator BinaryOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
bool CountFound = false;
SMPRegTransfer *TempRT = NULL;
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
SMPRegTransfer *LowerRightRT = new SMPRegTransfer;
LowerRightRT->SetParentInst(this);
// The doubleword shifts operate as follows: shift the DEF register right or left by
// the number of bits specified by the count, and shift in bits from the USE register,
// but do not change the USE register. This is hard to represent accurately in an RTL,
// so we create an RTL as follows:
//
// ASSIGN
// / \
// DEF SHIFT
// / \
// DEF SHIFT
// / \
// USE count
//
// This records all the operands, but it makes it look like the result of the
// lower shift is the counter for the upper shift, which is unintentional and
// will have to be special cased. In all single word shifts, the lower shift
// subtree would be replaced by a single count operand. The presence of a sub-tree
// instead of an operand is the identifying marker for double-word shifts.
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
for (OpNum = 0; !(DestFound && SourceFound && CountFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
LowerRightRT->SetOperator(BinaryOp);
RightRT->SetRightTree(LowerRightRT);
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
if (!SourceFound) {
SourceFound = true;
LowerRightRT->SetLeftOperand(TempOp);
}
else {
CountFound = true;
LowerRightRT->SetRightOperand(TempOp);
}
}
}
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound || !CountFound) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find double-shift operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
// The carry flag gets the last shifted out bit.
this->RTL.ExtraKills.push_back(FlagsOp);
}
return (DestFound && SourceFound && CountFound);
} // end of SMPInstr::BuildDoubleShiftRTL()
// Build the RTL for a multiply or divide, which can have implicit EAX and/or EDX operands
bool SMPInstr::BuildMultiplyDivideRTL(SMPoperator BinaryOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
bool HiddenEAXUse = false;
bool ImplicitEDXUse = false;
SMPRegTransfer *TempRT = NULL;
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
op_t FPRegOp = InitOp;
FPRegOp.type = o_fpreg; // floating point register stack
op_t Immed1Op = InitOp;
Immed1Op.type = o_imm; // immediate 1 for increment or decrement
FPRegOp.value = 1;
// Detect the cases in which EDX:EAX is the destination and EAX is a hidden operand.
// See detailed comments on the multiply and divide instructions in MDFixupDefUseLists().
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (!TempOp.showed()) { // hidden operand
if (TempOp.is_reg(R_ax)) { // not R_al, so it is not 8 bits
// This form always has a hidden use of EDX:EAX
HiddenEAXUse = true;
ImplicitEDXUse = true;
}
else if (TempOp.is_reg(R_al)) {
// Use of AX register to hold 16-bit result is hidden,
// but EDX is not needed to hold result bits.
HiddenEAXUse = true;
}
}
}
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
SourceFound = true;
RightRT->SetRightOperand(TempOp);
}
}
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound) {
assert(NULL != RightRT);
if (DestFound && (NULL != TempRT))
delete TempRT;
else
delete RightRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find mul/div operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
if (ImplicitEDXUse) {
// Need another effect for EDX, which was implicit.
// Make a deep copy from existing EAX effect and change EAX dest to EDX.
// For divisions, we also deep copy EAX effect and change EAX source to EDX.
SMPRegTransfer *EDXRT = new SMPRegTransfer;
EDXRT->SetParentInst(this);
SMPRegTransfer *EDXRightRT = new SMPRegTransfer;
EDXRightRT->SetParentInst(this);
op_t EDXOp;
EDXRT->SetOperator(SMP_ASSIGN);
EDXOp = TempRT->GetLeftOperand();
assert(EDXOp.is_reg(R_ax));
EDXOp.reg = R_dx;
EDXRT->SetLeftOperand(EDXOp);
op_t SourceOp = RightRT->GetLeftOperand();
if ((NN_div == this->SMPcmd.itype) || (NN_idiv == this->SMPcmd.itype)) {
// Need to change left operand of RightRT to EDX. i.e. we are
// changing the effect from eax := eax DIV foo to edx := edx DIV foo.
assert(SourceOp.is_reg(R_ax));
EDXRightRT->SetLeftOperand(EDXOp);
}
else { // just use same source operands for multiplies
EDXRightRT->SetLeftOperand(SourceOp);
}
EDXRightRT->SetOperator(BinaryOp);
EDXRightRT->SetRightOperand(RightRT->GetRightOperand());
EDXRT->SetRightTree(EDXRightRT);
this->RTL.push_back(EDXRT);
this->ResetMultiplicationBitsDiscarded();
}
else { // No implicit EDX effect.
// If we had 8x8=>16 bit multiply with AL*op8=>AX there
// is no discarding of result bits, else there is discarding.
if (!HiddenEAXUse)
this->SetMultiplicationBitsDiscarded();
}
}
return (DestFound && SourceFound);
} // end of SMPInstr::BuildMultiplyDivideRTL()
// Build the RTL for an instruction with a tertiary arithmetic opcode applied to
// two operands plus an implied FLAGS operand, e.g. add with carry adds the carry bit
// and two operands together; rotate through carry, etc.
bool SMPInstr::BuildBinaryPlusFlagsRTL(SMPoperator BinaryOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
SMPRegTransfer *TempRT = NULL;
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
SMPRegTransfer *FlagsRightRT = new SMPRegTransfer;
FlagsRightRT->SetParentInst(this);
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
SourceFound = true;
FlagsRightRT->SetLeftOperand(TempOp);
FlagsRightRT->SetOperator(BinaryOp);
FlagsRightRT->SetRightOperand(FlagsOp);
RightRT->SetRightTree(FlagsRightRT);
}
}
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound) {
if (DestFound)
delete TempRT; // also deletes linked in RightRT
else
delete RightRT; // will also delete FlagsRightRT if SourceFound is true
if (!SourceFound) // FlagsRightRT not linked into RightRT yet
delete FlagsRightRT; // .. so delete FlagsRightRT separately
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find binary operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
}
return (DestFound && SourceFound);
} // end of SMPInstr::BuildBinaryPlusFlagsRTL()
#define SMP_FIRST_SET_OPCODE NN_seta
#define SMP_LAST_SET_OPCODE NN_setz
// Build the RTL for an instruction of form dest := unary_operator(source), dest != source
bool SMPInstr::BuildUnary2OpndRTL(SMPoperator UnaryOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
int opcode = this->SMPcmd.itype;
bool ExtendedMove = ((NN_movsx == opcode) || (NN_movzx == opcode));
op_t VoidOp = InitOp;
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
op_t PortNumOp = InitOp;
PortNumOp.type = o_reg;
PortNumOp.reg = R_dx;
op_t PortDataOp = InitOp;
PortDataOp.type = o_reg;
PortDataOp.reg = R_ax;
// Handle special cases first.
if ((SMP_FIRST_SET_OPCODE <= opcode) && (SMP_LAST_SET_OPCODE >= opcode)) {
// Set instructions implicitly use the flags register.
SourceFound = true;
RightRT->SetLeftOperand(FlagsOp);
}
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
if (NN_in == opcode) {
#if 0
SMP_msg("ERROR: Explicit DEF for IN from port opcode at %x : ", this->GetAddr());
PrintOperand(TempOp);
SMP_msg("\n");
#endif
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(UnaryOp);
}
else if (NN_out == opcode) {
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(UnaryOp);
}
else {
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetRightOperand(VoidOp);
RightRT->SetOperator(UnaryOp);
TempRT->SetRightTree(RightRT);
}
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
SourceFound = true;
if (NN_in == opcode) {
TempRT->SetRightOperand(TempOp);
}
else if (NN_out == opcode) {
#if 0
SMP_msg("ERROR: Explicit USE for OUT to port opcode at %x : ", this->GetAddr());
PrintOperand(TempOp);
SMP_msg("\n");
#endif
TempRT->SetRightOperand(TempOp);
}
else {
RightRT->SetLeftOperand(TempOp);
if (ExtendedMove)
this->MoveSource = TempOp;
}
}
}
} // end for (OpNum = 0; ...)
if (!SourceFound && (NN_in == opcode)) {
// Input from port is implicitly from port # in DX register if not
// specified with an immediate operand.
SourceFound = true;
TempRT->SetRightOperand(PortNumOp);
}
if (!DestFound && (NN_in == opcode)) {
// Input from port is implicitly to register AL, AX, or EAX
// depending on the opcode and bit width mode.
// NOTE: Two different machine codes for 8-bit and 32-bit widths.
// We should check and set dtyp field accordingly.
DestFound = true;
TempRT->SetLeftOperand(PortDataOp);
TempRT->SetOperator(UnaryOp);
}
if (!DestFound && (NN_out == opcode)) {
// Output to port is implicitly to port # in DX register if not
// specified with an immediate operand.
// NOTE: Two different machine codes for 8-bit and 32-bit widths.
// We should check and set dtyp field accordingly.
DestFound = true;
TempRT->SetLeftOperand(PortNumOp);
TempRT->SetOperator(SMP_ASSIGN);
}
if (!SourceFound && (NN_out == opcode)) {
// Output to port is implicitly from register AL, AX, or EAX
// depending on the opcode and bit width mode.
SourceFound = true;
TempRT->SetRightOperand(PortDataOp);
}
if (!DestFound || !SourceFound) {
if (!DestFound)
delete RightRT; // never linked in to TempRT
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find binary operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
if ((NN_in == opcode) || (NN_out == opcode))
delete RightRT; // unused for port I/O
}
return (DestFound && SourceFound);
} // end of SMPInstr::BuildUnary2OpndRTL()
// Build the RTL for an instruction of form: dec ECX, loop to address with optional condition code guard
bool SMPInstr::BuildLoopRTL(SMPoperator GuardOp) {
size_t OpNum;
bool SourceFound = false;
SMPRegTransfer *LoopRT = new SMPRegTransfer;
LoopRT->SetParentInst(this);
SMPRegTransfer *DecCounterRT = new SMPRegTransfer;
DecCounterRT->SetParentInst(this);
op_t VoidOp = InitOp;
op_t OneOp = InitOp;
OneOp.type = o_imm; // immediate one
OneOp.value = 1;
op_t CountOp = InitOp;
CountOp.type = o_reg;
CountOp.reg = R_cx;
op_t EIPOp = InitOp;
EIPOp.type = o_reg;
EIPOp.reg = MD_INSTRUCTION_POINTER_REG;
// All x86 loop instructions behave as follows:
// 1. Decrement RCX/ECX/CX depending on 64/32/16-bit mode of CPU.
// 2. If counter decremented to zero, fall through to next instruction.
// 3. For condition code variants, fall through if zero flag is 1 or 0, depending on variant.
// 4. Otherwise, loop back by adding a signed 8-bit offset to the instruction counter.
//
// So, for LOOP, it is simply decrement ECX and loop back if ECX did not become zero.
// For LOOPE a.k.a. LOOPZ, it is decrement ECX and loop back if ECX != 0 and ZF == 1, which means
// that ZF (zero flag) was set to 1 by an instruction before the LOOPE instruction, not by the
// decrement of ECX within the microcode for LOOPE.
// For LOOPNE a.k.a. LOOPNZ, change ZF == 1 to ZF == 0 in above description.
//
// For RTLs, this means we need an RTL to decrement ECX, and a guarded RTL to add an immediate to the EIP reg.
// The guard can be simple (ECX != 0) or compound ((ECX != 0) AND (ZF is 0 or 1)). However, because RTs in
// an RTL happen in parallel, we compare ECX to 1 because the comparison happens before it is decremented by
// another RT in the RTL. Note also that IDA Pro has converted the signed 8-bit relative offset to a near address
// in the operand list.
DecCounterRT->SetLeftOperand(CountOp);
DecCounterRT->SetOperator(SMP_DECREMENT);
DecCounterRT->SetRightOperand(VoidOp);
for (OpNum = 0; (!SourceFound && (OpNum < UA_MAXOP)); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: Unexpected DEF Operand in LOOP instruction: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
;
}
if (!SourceFound && (this->features & UseMacros[OpNum]) && MDKnownOperandType(TempOp)) {
if (o_near == TempOp.type) {
LoopRT->SetLeftOperand(EIPOp);
LoopRT->SetOperator(SMP_ASSIGN);
LoopRT->SetRightOperand(TempOp);
SourceFound = true;
}
}
} // end for (OpNum = 0; ...)
if (!SourceFound) {
if (NULL != LoopRT)
delete LoopRT;
if (NULL != DecCounterRT)
delete DecCounterRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find loop operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
// If the move is conditional, set the guard expression.
// NOTE: We do not want to complicate the SMPGuard class to handle compound expressions yet.
// If the need arises, we can do so. For now, just treat all loop instructions as a simple LOOP
// even if it is LOOPE or LOOPNE.
SMPGuard *Guard1 = new SMPGuard;
Guard1->SetLeftOperand(CountOp);
Guard1->SetOperator(SMP_EQUAL);
Guard1->SetRightOperand(OneOp);
LoopRT->SetGuard(Guard1);
this->RTL.push_back(DecCounterRT);
this->RTL.push_back(LoopRT);
}
return (SourceFound);
} // end of SMPInstr::BuildLoopRTL()
// Build the RTL for an instruction of form dest := source, where dest != source
bool SMPInstr::BuildMoveRTL(SMPoperator GuardOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
bool MemSrc = this->HasSourceMemoryOperand();
bool MemDest = this->HasDestMemoryOperand();
bool HasRepeatPrefix = (0 != (this->SMPcmd.auxpref & aux_rep))
|| (0 != (this->SMPcmd.auxpref & aux_repne));
int opcode = this->SMPcmd.itype;
#if IDA_SDK_VERSION < 600
if ((NN_ldmxcsr == opcode) || (NN_stmxcsr == opcode)) {
// IDA 5.1 does not have the R_mxcsr enumeration value,
// so we cannot handle these opcodes.
return false;
}
#endif
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
op_t VoidOp = InitOp;
op_t EAXOp = InitOp;
EAXOp.type = o_reg;
EAXOp.reg = R_ax;
op_t ALOp = InitOp;
ALOp.type = o_reg;
ALOp.reg = R_al;
ALOp.dtyp = dt_byte;
op_t CountOp = InitOp;
CountOp.type = o_reg;
CountOp.reg = R_cx;
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
op_t FPRegOp = InitOp;
FPRegOp.type = o_fpreg; // floating point register stack
FPRegOp.reg = 0;
op_t PortNumOp = InitOp;
PortNumOp.type = o_reg;
PortNumOp.reg = R_dx;
op_t PortDataOp = InitOp;
PortDataOp.type = o_reg;
PortDataOp.reg = R_ax;
#if IDA_SDK_VERSION > 599
op_t MXCSROp = InitOp; // MMX Control & Status Register
MXCSROp.type = o_reg;
MXCSROp.reg = R_mxcsr;
#endif
op_t ZeroOp = InitOp;
ZeroOp.type = o_imm; // immediate zero
ZeroOp.value = 0;
#if SMP_DEBUG_BUILD_RTL
if (MemSrc && MemDest && (NN_movs != opcode) && (NN_movss != opcode) && (NN_movsd != opcode)) {
if ((NN_stos != opcode) && (NN_outs != opcode)) {
SMP_msg("ERROR: IDA Pro error: MemDest and MemSrc in move at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
#if 1
else { // IDA incorrectly lists [EDI] as both DEF and USE, because reg EDI
// is both DEF and USE in NN_stos.
SMP_msg("WARNING: Ignoring IDA Pro error: MemDest and MemSrc in move at %x for %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
this->PrintOperands();
}
#endif
}
#endif
// First, handle special cases with implicit operands
if (NN_lahf == opcode) { // load AH from flags
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(EAXOp);
TempRT->SetRightOperand(FlagsOp);
this->RTL.push_back(TempRT);
return true;
}
if (NN_sahf == opcode) { // store AH to flags
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(FlagsOp);
TempRT->SetRightOperand(EAXOp);
this->RTL.push_back(TempRT);
return true;
}
if ((NN_movs == opcode) || (NN_stos == opcode) || (NN_ins == opcode) || (NN_outs == opcode)) {
// The ESI and EDI registers get incremented or decremented, depending
// on the direction flag DF, for MOVS; only EDI for STOS and INS;
// only ESI for OUTS.
// This is true with or without a repeat prefix.
op_t ESIOp = InitOp, EDIOp = InitOp;
ESIOp.type = o_reg;
ESIOp.reg = R_si;
EDIOp.type = o_reg;
EDIOp.reg = R_di;
op_t ESIMemOp = InitOp, EDIMemOp = InitOp; // [esi] and [edi]
ESIMemOp.type = o_phrase;
ESIMemOp.reg = R_si;
EDIMemOp.type = o_phrase;
EDIMemOp.reg = R_di;
if (NN_movs == opcode) {
this->RTL.ExtraKills.push_back(ESIOp);
this->RTL.ExtraKills.push_back(EDIOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(EDIMemOp);
TempRT->SetRightOperand(ESIMemOp);
DestFound = true;
SourceFound = true;
}
else if (NN_stos == opcode) {
this->RTL.ExtraKills.push_back(EDIOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(EDIMemOp);
TempRT->SetRightOperand(ALOp); // default in case we don't find source later
DestFound = true;
}
else if (NN_ins == opcode) {
this->RTL.ExtraKills.push_back(EDIOp);
TempRT->SetOperator(SMP_INPUT);
TempRT->SetLeftOperand(EDIMemOp);
TempRT->SetRightOperand(PortNumOp);
DestFound = true;
SourceFound = true;
}
else if (NN_outs == opcode) {
this->RTL.ExtraKills.push_back(ESIOp);
TempRT->SetOperator(SMP_OUTPUT);
TempRT->SetLeftOperand(ESIMemOp);
TempRT->SetRightOperand(PortNumOp);
DestFound = true;
SourceFound = true;
}
}
// Some floating point instructions use the floating point register stack top as
// an implicit source or destination, but the other operand of the load or store
// is explicit, so we set the implicit operand and let control flow pass to the
// main processing loop below.
if ((NN_fld == opcode) || (NN_fbld == opcode) || (NN_fild == opcode)) {
// Loads implicitly use the floating point stack top as destination.
TempRT->SetLeftOperand(FPRegOp);
TempRT->SetOperator(SMP_ASSIGN);
DestFound = true;
}
else if ((NN_fst == opcode) || (NN_fstp == opcode) || (NN_fbstp == opcode)
|| (NN_fist == opcode) || (NN_fistp == opcode)) {
// Stores implicitly use the floating point stack top as source
TempRT->SetRightOperand(FPRegOp);
SourceFound = true;
// The "p" at the end of the opcode indicates that the floating point
// register stack gets popped.
if ((NN_fstp == opcode) || (NN_fbstp == opcode) || (NN_fistp == opcode)) {
this->RTL.ExtraKills.push_back(FPRegOp);
}
}
#if IDA_SDK_VERSION > 599
else if (NN_ldmxcsr == opcode) {
// The MMX Control & Status Register is used implicitly.
TempRT->SetLeftOperand(MXCSROp);
DestFound = true;
}
else if (NN_stmxcsr == opcode) {
// The MMX Control & Status Register is used implicitly.
TempRT->SetRightOperand(MXCSROp);
SourceFound = true;
}
#endif
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (!DestFound && MDKnownOperandType(TempOp)) {
// See comments just below for floating point sources. FP stores
// are analogous to FP loads.
if (!MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
DestFound = true;
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
}
}
}
else { // USE
if (!SourceFound && MDKnownOperandType(TempOp)) {
// If this is a floating point instruction with the fpregs listed as
// a USE and a memory operand also listed as a USE, then we want to
// ignore the irrelevant USE of the fpreg stack.
// Note that MemDest AND MemSrc means something like stosb is being
// processed, where the memory operand is both DEF and USE to IDA
// for mysterious reasons.
if (!MemSrc || MemDest || ((TempOp.type >= o_mem) && (TempOp.type <= o_displ))) {
SourceFound = true;
TempRT->SetRightOperand(TempOp);
this->MoveSource = TempOp;
}
}
if (this->features & UseMacros[OpNum]) {
;
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: Operand neither DEF nor USE: ");
PrintOperand(TempOp);
SMP_msg(" at %x in %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find move operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
// If the move is conditional, set the guard expression.
if (SMP_NULL_OPERATOR != GuardOp) {
SMPGuard *Guard1 = new SMPGuard;
Guard1->SetLeftOperand(FlagsOp);
Guard1->SetOperator(GuardOp);
Guard1->SetRightOperand(ZeroOp);
TempRT->SetGuard(Guard1);
if (this->MDIsConditionalMoveInstr()) {
// We need to represent the possibility that the DEF operand will not
// be set because the move is conditional. We will add the DEF operand
// into the USE set and special case our type inferences so that the
// USE and the pseudo-USE (prior SSA value of the DEF operand) must
// agree in type before we can be sure of the result type.
assert(this->Defs.GetSize() == 1);
this->Uses.SetRef(this->Defs.GetFirstRef()->GetOp());
}
}
this->RTL.push_back(TempRT);
// Now, create the repeat prefix effects
if (HasRepeatPrefix) { // Must be MOVS or STOS or INS or OUTS
// The repeat causes USE and DEF of ECX as a counter
SMPRegTransfer *CounterRT = new SMPRegTransfer;
CounterRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
CounterRT->SetLeftOperand(CountOp);
CounterRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(CountOp);
RightRT->SetOperator(SMP_UNARY_NUMERIC_OPERATION);
RightRT->SetRightOperand(VoidOp);
CounterRT->SetRightTree(RightRT);
this->RTL.push_back(CounterRT);
}
}
return (DestFound && SourceFound);
} // end of SMPInstr::BuildLoopRTL()
// Build the RTL for a load string instruction, which loads from ES:ESI into EAX
// (or into AX or AL) and increments or decrements ESI based on the direction flag DF.
bool SMPInstr::BuildLoadStringRTL(void) {
bool DestFound = false;
unsigned short ByteSize = 0;
op_t DestOp = InitOp;
DestOp.type = o_reg;
DestOp.reg = R_al;
for (size_t OpNum = 0; !DestFound && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (MDKnownOperandType(TempOp)) {
if ((o_reg == TempOp.type) && (this->features & DefMacros[OpNum])) { // DEF
DestFound = true;
if (TempOp.is_reg(R_al)) {
ByteSize = 1;
}
else if (TempOp.is_reg(R_ax)) {
ByteSize = 4;
DestOp.reg = R_ax;
}
else {
SMP_msg("ERROR: Load string destination operand is neither AL nor EAX at %x\n", this->GetAddr());
ByteSize = 1; // default to AL destination
}
}
}
} // end for (OpNum = 0; ...)
// Return false if we did not find a destination
if (!DestFound) {
return false;
}
op_t ZeroOp = InitOp;
ZeroOp.type = o_imm; // immediate zero
ZeroOp.value = 0;
op_t OneOp = InitOp;
OneOp.type = o_imm; // immediate one
OneOp.value = 1;
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
op_t ESIOp = InitOp;
ESIOp.type = o_reg;
ESIOp.reg = R_si;
op_t DerefESIOp = InitOp;
DerefESIOp.type = o_phrase;
DerefESIOp.reg = R_si;
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
SMPRegTransfer *GuardedIncRT = new SMPRegTransfer;
GuardedIncRT->SetParentInst(this);
SMPRegTransfer *GuardedDecRT = new SMPRegTransfer;
GuardedDecRT->SetParentInst(this);
SMPRegTransfer *RightIncRT = new SMPRegTransfer;
RightIncRT->SetParentInst(this);
SMPRegTransfer *RightDecRT = new SMPRegTransfer;
RightDecRT->SetParentInst(this);
// Build the load string RTL. Ignore ES segment register for now.
// Load string is: AL := [ESI]; if (DF == 0) ESI += 1 else ESI -= 1;
// for the 8-bit case, and EAX := [ESI]; if (DF == 0) ESI += 4 else ESI -= 4;
// for the 32-bit case.
// AL := [ESI] or EAX := [ESI]
TempRT->SetLeftOperand(DestOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(DerefESIOp);
this->RTL.push_back(TempRT);
// Guarded increment RTL: If the DF (Direction Flag) in EFLAGS is 0, increment ESI
// by ByteSize.
op_t IncDecOp = InitOp;
IncDecOp.type = o_imm;
IncDecOp.value = ByteSize;
SMPGuard *Guard1 = new SMPGuard;
Guard1->SetLeftOperand(FlagsOp);
Guard1->SetOperator(SMP_U_COMPARE);
Guard1->SetRightOperand(ZeroOp);
GuardedIncRT->SetGuard(Guard1);
GuardedIncRT->SetLeftOperand(ESIOp);
GuardedIncRT->SetOperator(SMP_ASSIGN);
RightIncRT->SetLeftOperand(ESIOp);
RightIncRT->SetOperator(SMP_ADD);
RightIncRT->SetRightOperand(IncDecOp);
GuardedIncRT->SetRightTree(RightIncRT);
this->RTL.push_back(GuardedIncRT);
// Guarded decrement RTL: If the DF (Direction Flag) in EFLAGS is 1, decrement ESI
// by ByteSize.
SMPGuard *Guard2 = new SMPGuard;
Guard2->SetLeftOperand(FlagsOp);
Guard2->SetOperator(SMP_U_COMPARE);
Guard2->SetRightOperand(OneOp);
GuardedDecRT->SetGuard(Guard2);
GuardedDecRT->SetLeftOperand(ESIOp);
GuardedDecRT->SetOperator(SMP_ASSIGN);
RightDecRT->SetLeftOperand(ESIOp);
RightDecRT->SetOperator(SMP_SUBTRACT);
RightDecRT->SetRightOperand(IncDecOp);
GuardedDecRT->SetRightTree(RightDecRT);
this->RTL.push_back(GuardedDecRT);
return true;
} // end of SMPInstr::BuildLoadStringRTL()
// Build the RTL for a compare string instruction, possibly with repeat prefix.
bool SMPInstr::BuildCompareStringRTL(void) {
size_t OpNum;
bool Src1Found = false;
bool Src2Found = false;
bool HasRepeatPrefix = (0 != (this->SMPcmd.auxpref & aux_rep))
|| (0 != (this->SMPcmd.auxpref & aux_repne));
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
op_t CountOp = InitOp;
CountOp.type = o_reg;
CountOp.reg = R_cx;
op_t VoidOp = InitOp;
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
for (OpNum = 0; !(Src1Found && Src2Found) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (MDKnownOperandType(TempOp)) {
if (!Src1Found) {
Src1Found = true;
TempRT->SetLeftOperand(FlagsOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(SMP_U_COMPARE);
TempRT->SetRightTree(RightRT);
if (this->features & DefMacros[OpNum]) // DEF
SMP_msg("CMPS 1st opnd is DEF\n");
else if (this->features & UseMacros[OpNum]) // USE
SMP_msg("CMPS 1st opnd is USE\n");
else
SMP_msg("CMPS 1st opnd neither DEF nor USE\n");
}
else {
Src2Found = true;
RightRT->SetRightOperand(TempOp);
if (this->features & DefMacros[OpNum]) // DEF
SMP_msg("CMPS 2nd opnd is DEF\n");
else if (this->features & UseMacros[OpNum]) // USE
SMP_msg("CMPS 2nd opnd is USE\n");
else
SMP_msg("CMPS 2nd opnd neither DEF nor USE\n");
}
}
} // end for (OpNum = 0; ...)
if (!Src1Found || !Src2Found) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find CMPS operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
// Now, create the repeat prefix effects
if (HasRepeatPrefix) {
// The repeat causes USE and DEF of ECX as a counter
SMPRegTransfer *CounterRT = new SMPRegTransfer;
CounterRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
CounterRT->SetLeftOperand(CountOp);
CounterRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(CountOp);
RightRT->SetOperator(SMP_UNARY_NUMERIC_OPERATION);
RightRT->SetRightOperand(VoidOp);
CounterRT->SetRightTree(RightRT);
this->RTL.push_back(CounterRT);
}
}
return (Src1Found && Src2Found);
} // end of SMPInstr::BuildCompareStringRTL()
// Build the RTL for an instruction of form dest := source, source := dest
bool SMPInstr::BuildExchangeRTL(void) {
size_t OpNum;
bool Src1Found = false;
bool Src2Found = false;
SMPRegTransfer *TempRT = new SMPRegTransfer; // second effect, src := dest
TempRT->SetParentInst(this);
for (OpNum = 0; !(Src1Found && Src2Found) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (MDKnownOperandType(TempOp)) {
if (!Src1Found) {
Src1Found = true;
TempRT->SetRightOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
#if SMP_VERBOSE_DEBUG_BUILD_RTL
if (this->features & DefMacros[OpNum]) // DEF
SMP_msg("XCHG 1st opnd is DEF\n");
else if (this->features & UseMacros[OpNum]) // USE
SMP_msg("XCHG 1st opnd is USE\n");
else
SMP_msg("XCHG 1st opnd neither DEF nor USE\n");
#endif
}
else {
Src2Found = true;
TempRT->SetLeftOperand(TempOp);
if (this->features & DefMacros[OpNum]) // DEF
SMP_msg("XCHG 2nd opnd is DEF\n");
else if (this->features & UseMacros[OpNum]) // USE
SMP_msg("XCHG 2nd opnd is USE\n");
else
SMP_msg("XCHG 2nd opnd neither DEF nor USE\n");
}
}
} // end for (OpNum = 0; ...)
if (!Src1Found || !Src2Found) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find XCHG operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
// Create the first effect, dest := src
SMPRegTransfer *FirstRT = new SMPRegTransfer;
FirstRT->SetParentInst(this);
FirstRT->SetLeftOperand(TempRT->GetRightOperand());
FirstRT->SetRightOperand(TempRT->GetLeftOperand());
FirstRT->SetOperator(SMP_ASSIGN);
this->RTL.push_back(FirstRT);
// Push the second effect on the list, src := dest
this->RTL.push_back(TempRT);
}
return (Src1Found && Src2Found);
} // end of SMPInstr::BuildExchangeRTL()
// Build the RTL for an instruction of form dest := dest + source, source := dest
bool SMPInstr::BuildExchangeAddRTL(void) {
size_t OpNum;
bool Src1Found = false;
bool Src2Found = false;
SMPRegTransfer *TempRT = new SMPRegTransfer; // second effect, src := dest
TempRT->SetParentInst(this);
for (OpNum = 0; !(Src1Found && Src2Found) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (MDKnownOperandType(TempOp)) {
if (!Src1Found) {
Src1Found = true;
TempRT->SetRightOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
if (this->features & DefMacros[OpNum]) // DEF
SMP_msg("XADD 1st opnd is DEF\n"); // should be the case
else if (this->features & UseMacros[OpNum]) // USE
SMP_msg("WARNING: XADD 1st opnd is USE\n");
else
SMP_msg("WARNING: XADD 1st opnd neither DEF nor USE\n");
}
else {
Src2Found = true;
TempRT->SetLeftOperand(TempOp);
if (this->features & DefMacros[OpNum]) // DEF
SMP_msg("WARNING: XADD 2nd opnd is DEF\n");
else if (this->features & UseMacros[OpNum]) // USE
SMP_msg("XADD 2nd opnd is USE\n"); // should be the case
else
SMP_msg("WARNING: XADD 2nd opnd neither DEF nor USE\n");
}
}
} // end for (OpNum = 0; ...)
if (!Src1Found || !Src2Found) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find XADD operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
// Create the first effect, dest := dest + src
SMPRegTransfer *FirstRT = new SMPRegTransfer;
FirstRT->SetParentInst(this);
SMPRegTransfer *AddRT = new SMPRegTransfer;
AddRT->SetParentInst(this);
AddRT->SetLeftOperand(TempRT->GetRightOperand());
AddRT->SetOperator(SMP_ADD);
AddRT->SetRightOperand(TempRT->GetLeftOperand());
FirstRT->SetLeftOperand(TempRT->GetRightOperand());
FirstRT->SetRightTree(AddRT);
FirstRT->SetOperator(SMP_ASSIGN);
this->RTL.push_back(FirstRT);
// Push the second effect on the list, src := dest
this->RTL.push_back(TempRT);
}
return (Src1Found && Src2Found);
} // end of SMPInstr::BuildExchangeAddRTL()
// Build the RTL for an instruction of form:
// if (dest==EAX) dest := source else EAX := dest
bool SMPInstr::BuildCompareExchangeRTL(void) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
op_t DestOp = InitOp;
op_t SourceOp = InitOp;
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
for (OpNum = 0; !(DestFound && SourceFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (MDKnownOperandType(TempOp)) {
if (this->features & DefMacros[OpNum]) { // DEF
if (!DestFound) {
DestFound = true;
DestOp = TempOp;
}
else {
SMP_msg("CMPXCHG has two DEF operands.\n");
}
}
else if (this->features & UseMacros[OpNum]) { // USE
if (!SourceFound) {
SourceFound = true;
SourceOp = TempOp;
}
else {
SMP_msg("CMPXCHG has two USE operands.\n");
}
}
}
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find CMPXCHG operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
// Create the first effect, if (dest == EAX) dest := src
SMPGuard *Guard1 = new SMPGuard;
op_t EAXOp = InitOp;
EAXOp.type = o_reg;
EAXOp.reg = R_ax;
Guard1->SetLeftOperand(DestOp);
Guard1->SetOperator(SMP_EQUAL);
Guard1->SetRightOperand(EAXOp);
SMPRegTransfer *FirstRT = new SMPRegTransfer;
FirstRT->SetParentInst(this);
FirstRT->SetLeftOperand(DestOp);
FirstRT->SetRightOperand(SourceOp);
FirstRT->SetOperator(SMP_ASSIGN);
FirstRT->SetGuard(Guard1);
this->RTL.push_back(FirstRT);
// Push the second effect on the list, if (dest!=EAX) dest := EAX
SMPGuard *Guard2 = new SMPGuard;
Guard2->SetLeftOperand(DestOp);
Guard2->SetOperator(SMP_EQUAL);
Guard2->SetRightOperand(EAXOp);
TempRT->SetLeftOperand(DestOp);
TempRT->SetRightOperand(EAXOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetGuard(Guard2);
this->RTL.push_back(TempRT);
}
return (DestFound && SourceFound);
} // end of SMPInstr::BuildCompareExchangeRTL()
// Build the RTL for an extended FP concatenate and shift instruction
bool SMPInstr::BuildPackShiftRTL(SMPoperator PackOp, SMPoperator ShiftOp) {
size_t OpNum;
bool DestFound = false;
bool SourceFound = false;
bool CountFound = false;
SMPRegTransfer *TempRT = NULL;
SMPRegTransfer *ShiftRT = new SMPRegTransfer;
ShiftRT->SetParentInst(this);
SMPRegTransfer *PackRT = new SMPRegTransfer;
PackRT->SetParentInst(this);
// RTL structure: top operator is assignment, next right operator is a reverse
// shift with the shift count as its left operand, and lowest right operator
// is the concatenation operator. Sequence of operations is pack, shift, assign.
for (OpNum = 0; !(DestFound && SourceFound && CountFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
PackRT->SetLeftOperand(TempOp);
PackRT->SetOperator(PackOp);
ShiftRT->SetOperator(ShiftOp);
ShiftRT->SetRightTree(PackRT);
TempRT->SetRightTree(ShiftRT);
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
if (!SourceFound) {
SourceFound = true;
PackRT->SetRightOperand(TempOp);
}
else {
CountFound = true;
ShiftRT->SetLeftOperand(TempOp);
}
}
}
} // end for (OpNum = 0; ...)
if (!DestFound || !SourceFound || !CountFound) {
if (NULL != TempRT)
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find MMX/XMM pack and shift operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
}
return (DestFound && SourceFound && CountFound);
} // end of SMPInstr::BuildPackShiftRTL()
// Build the RTL for a compare or test instruction with an implicit EFLAGS destination operand
bool SMPInstr::BuildFlagsDestBinaryRTL(SMPoperator BinaryOp) {
size_t OpNum;
int opcode = this->SMPcmd.itype;
bool Source1Found = false;
bool Source2Found = false;
bool NoOperandsRequired = ((NN_scas == opcode) || (NN_cmps == opcode));
bool HasRepeatPrefix = (0 != (this->SMPcmd.auxpref & aux_rep))
|| (0 != (this->SMPcmd.auxpref & aux_repne));
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
op_t VoidOp = InitOp, FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
op_t CountOp = InitOp;
CountOp.type = o_reg;
CountOp.reg = R_cx;
op_t FPRegOp = InitOp;
FPRegOp.type = o_fpreg; // floating point register stack
FPRegOp.reg = 0;
// Some floating point instructions use the floating point register stack top as
// an implicit source or destination, but the other operand of the load or store
// is explicit, so we set the implicit operand and let control flow pass to the
// main processing loop below.
if ((NN_fcomi == opcode) || (NN_fucomi == opcode) || (NN_fcomip == opcode)
|| (NN_fucomip == opcode)) {
// Compares implicitly use the floating point stack top as destination.
TempRT->SetLeftOperand(FlagsOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(FPRegOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
Source1Found = true;
// The "p" at the end of the opcode indicates that the floating point
// register stack gets popped.
if ((NN_fcomip == opcode) || (NN_fucomip == opcode)) {
this->RTL.ExtraKills.push_back(FPRegOp);
}
}
for (OpNum = 0; !(Source1Found && Source2Found) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found destination for compare or test at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
if (!Source1Found) {
Source1Found = true;
TempRT->SetLeftOperand(FlagsOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(TempOp);
RightRT->SetOperator(BinaryOp);
TempRT->SetRightTree(RightRT);
}
else {
assert(!Source2Found);
Source2Found = true;
RightRT->SetRightOperand(TempOp);
}
}
}
} // end for (OpNum = 0; ...)
// The compare string instruction always uses DS:ESI and ES:EDI as its source
// operands, regardless of the explicit operands given, and might not have
// explicit operands; explicit operands are just for documentation.
// The scan string instruction uses EAX/AX/AH/AL and ES:EDI as its source
// operands and might not have any explicit operands at all.
if ((!NoOperandsRequired) && (!Source1Found || !Source2Found)) {
if (!Source1Found)
delete RightRT;
else
delete TempRT;
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find CMP/TEST operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
// Now, create the repeat prefix effects
if (HasRepeatPrefix) { // Must be CMPS or SCAS
// The repeat causes USE and DEF of ECX as a counter
SMPRegTransfer *CounterRT = new SMPRegTransfer;
CounterRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
CounterRT->SetLeftOperand(CountOp);
CounterRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(CountOp);
RightRT->SetOperator(SMP_UNARY_NUMERIC_OPERATION);
RightRT->SetRightOperand(VoidOp);
CounterRT->SetRightTree(RightRT);
this->RTL.push_back(CounterRT);
}
if ((NN_cmps == opcode) || (NN_scas == opcode)) {
// The ESI and EDI registers get incremented or decremented, depending
// on the direction flag DF, for CMPS; only EDI for SCAS.
// This is true with or without a repeat prefix.
op_t ESIOp = InitOp, EDIOp = InitOp;
ESIOp.type = o_reg;
ESIOp.reg = R_si;
EDIOp.type = o_reg;
EDIOp.reg = R_di;
if (NN_cmps == opcode) {
this->RTL.ExtraKills.push_back(ESIOp);
}
this->RTL.ExtraKills.push_back(EDIOp);
}
}
return (NoOperandsRequired || (Source1Found && Source2Found));
} // end of SMPInstr::BuildFlagsDestBinaryRTL()
// Build the RTL for a direct or indirect call instruction
bool SMPInstr::BuildCallRTL(void) {
size_t OpNum;
bool SourceFound = false;
op_t VoidOp = InitOp;
SMPRegTransfer *TempRT = NULL;
for (OpNum = 0; !SourceFound && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found destination operand for call at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
SourceFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(VoidOp);
TempRT->SetOperator(SMP_CALL);
TempRT->SetRightOperand(TempOp);
}
}
} // end for (OpNum = 0; ...)
if (!SourceFound) {
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find CALL operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
this->RTL.push_back(TempRT);
}
return SourceFound;
} // end of SMPInstr::BuildCallRTL()
// Build the RTL for a return instruction, with or without extra bytes popped off stack
bool SMPInstr::BuildReturnRTL(void) {
size_t OpNum;
uval_t PopBytes = 4; // default: pop off return address
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found destination operand for RET at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
if (o_imm == TempOp.type) {
PopBytes += TempOp.value;
}
else {
#if SMP_DEBUG_BUILD_RTL
if (!(this->IsTailCall())) {
SMP_msg("ERROR: Found unexpected operand for return at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
#endif
}
}
}
} // end for (OpNum = 0; ...)
this->AddToStackPointer(PopBytes);
return true;
} // end of SMPInstr::BuildReturnRTL()
// Build the RTL for an ENTER instruction
bool SMPInstr::BuildEnterRTL(void) {
// An "ENTER k,0" instruction with allocation k and nesting level 0 does the following:
// push ebp
// mov ebp,esp
// sub esp,k
// This can be modeled by the parallel effects:
// [esp-4] := ebp; ebp := esp - 4; esp := esp - (k + 4)
// If nesting level is greater than zero, we have a block structure language with
// nested procedures, in which additional frame pointers are saved:
// "ENTER k,n" pushes n additional frame pointers on the stack. We will only model
// the change in the stack pointer here, and not attempt to transfer the display
// pointers. A warning will be issued to the log file. Parallel effects are:
// [esp-4] := ebp; ebp := esp - 4; esp := esp - (((k + n)*4)+4)
// Note that k and n are immediate values so the final expression can be computed.
size_t OpNum;
uval_t NestingLevel = 0;
uval_t AllocBytes = 0;
bool AllocFound = false;
bool NestingLevelFound = false;
op_t StackPointerOp = InitOp; // ESP
StackPointerOp.type = o_reg;
StackPointerOp.reg = R_sp;
op_t FramePointerOp = InitOp; // EBP
FramePointerOp.type = o_reg;
FramePointerOp.reg = R_bp;
op_t Immed4Op = InitOp; // 4
Immed4Op.type = o_imm;
Immed4Op.value = 4;
op_t SavedEBP = InitOp; // [ESP-4], location of saved EBP
SavedEBP.type = o_displ;
SavedEBP.addr = (ea_t) -4;
SavedEBP.reg = R_sp;
for (OpNum = 0; !(AllocFound && NestingLevelFound) && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found destination operand for ENTER at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
else { // USE
if (MDKnownOperandType(TempOp)) {
if (o_imm == TempOp.type) {
if (!AllocFound) {
AllocBytes = TempOp.value;
AllocFound = true;
}
else {
NestingLevel = TempOp.value;
NestingLevelFound = true;
}
}
else {
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Found unexpected operand for ENTER at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
}
}
} // end for (OpNum = 0; ...)
if (!AllocFound) {
#if SMP_DEBUG_BUILD_RTL
SMP_msg("ERROR: Could not find allocation operand for ENTER at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
#endif
}
else {
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
// Add first effect: [esp-4] := ebp
TempRT->SetLeftOperand(SavedEBP);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(FramePointerOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// Add second effect: ebp := esp - 4
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(FramePointerOp);
TempRT->SetOperator(SMP_ASSIGN);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
RightRT->SetLeftOperand(StackPointerOp);
RightRT->SetOperator(SMP_SUBTRACT);
RightRT->SetRightOperand(Immed4Op);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
TempRT = NULL;
RightRT = NULL;
// Add final effect on stack pointer
AllocBytes += ((4 * NestingLevel) + 4);
if (0 != NestingLevel) {
SMP_msg("WARNING: Nested procedures in ENTER instruction at %x : %s\n",
this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
this->SubFromStackPointer(AllocBytes);
}
return AllocFound;
} // end of SMPInstr::BuildEnterRTL()
// Build the RTL for an LEAVE instruction
bool SMPInstr::BuildLeaveRTL(void) {
// A LEAVE instruction simulates the following instructions:
// mov ebp into esp (deallocates stack frame)
// pop saved ebp off stack into ebp
// We want to model these two instructions with three parallel effects:
// esp := ebp; ebp := [ebp+0]; esp = esp + 4;
// There cannot be two definitions of esp in the list of effects, so we do:
// esp := ebp + 4; ebp := [ebp+0] as our two parallel effects
op_t StackPointerOp = InitOp; // ESP
StackPointerOp.type = o_reg;
StackPointerOp.reg = R_sp;
op_t FramePointerOp = InitOp; // EBP
FramePointerOp.type = o_reg;
FramePointerOp.reg = R_bp;
op_t Immed4Op = InitOp; // 4
Immed4Op.type = o_imm;
Immed4Op.value = 4;
op_t SavedEBP = InitOp; // [EBP+0]
SavedEBP.type = o_displ;
SavedEBP.reg = R_bp;
// Build first effect: ESP := EBP + 4
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(StackPointerOp);
TempRT->SetOperator(SMP_ASSIGN);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
RightRT->SetOperator(SMP_ADD);
RightRT->SetLeftOperand(FramePointerOp);
RightRT->SetRightOperand(Immed4Op);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
TempRT = NULL;
RightRT = NULL;
// Build second effect: EBP := [EBP+0]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(FramePointerOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(SavedEBP);
this->RTL.push_back(TempRT);
TempRT = NULL;
return true;
} // end of SMPInstr::BuildLeaveRTL()
// Build OptCategory 8 RTLs, which set system info into EDX:EAX.
bool SMPInstr::BuildOptType8RTL(void) {
op_t DestOp = InitOp;
DestOp.type = o_reg;
op_t VoidOp = InitOp;
op_t LeftOp = InitOp;
if (NN_xgetbv == this->SMPcmd.itype) {
// AMD xgetbc opcode gets control register number to read out of ECX.
LeftOp.type = o_reg;
LeftOp.reg = R_cx;
}
// Create the effect on EDX.
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
DestOp.reg = R_dx;
TempRT->SetLeftOperand(DestOp);
TempRT->SetOperator(SMP_ASSIGN);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
RightRT->SetLeftOperand(LeftOp);
RightRT->SetOperator(SMP_SYSTEM_OPERATION);
RightRT->SetRightOperand(VoidOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
// Create the effect on EAX.
TempRT = NULL;
RightRT = NULL;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
DestOp.reg = R_ax;
TempRT->SetLeftOperand(DestOp);
TempRT->SetOperator(SMP_ASSIGN);
RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
RightRT->SetLeftOperand(LeftOp);
RightRT->SetOperator(SMP_SYSTEM_OPERATION);
RightRT->SetRightOperand(VoidOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
return true;
} // end of BuildOptType8RTL()
// Build the RTL for a direct or indirect jump instruction
bool SMPInstr::BuildJumpRTL(SMPoperator CondBranchOp) {
size_t OpNum;
bool TargetFound = false;
SMPRegTransfer *TempRT = NULL;
op_t EIPOp = InitOp, ZeroOp = InitOp, FlagsOp = InitOp;
EIPOp.type = o_reg;
EIPOp.reg = R_ip;
ZeroOp.type = o_imm;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
op_t CountOp = InitOp;
CountOp.type = o_reg;
CountOp.reg = R_cx;
#if 0
if (this->IsTailCall())
return this->BuildReturnRTL();
#endif
for (OpNum = 0; !TargetFound && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & UseMacros[OpNum]) { // USE
if (MDKnownOperandType(TempOp)) {
TargetFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(EIPOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(TempOp);
if (CondBranchOp != SMP_NULL_OPERATOR) {
// Set up a guard expression comparing EFLAGS to zero.
// NOTE: This is imprecise for value-set purposes, but OK for types.
SMPGuard *BranchCondition = new SMPGuard;
BranchCondition->SetOperator(CondBranchOp);
// The conditional jumps on ECX==0 compare to ECX, not EFLAGS.
if ((NN_jcxz <= this->SMPcmd.itype) && (NN_jrcxz >= this->SMPcmd.itype))
BranchCondition->SetLeftOperand(CountOp);
else
BranchCondition->SetLeftOperand(FlagsOp);
BranchCondition->SetRightOperand(ZeroOp);
TempRT->SetGuard(BranchCondition);
}
this->RTL.push_back(TempRT);
}
}
} // end for (OpNum = 0; ...)
#if SMP_DEBUG_BUILD_RTL
if (!TargetFound) {
SMP_msg("ERROR: Could not find jump target at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
#endif
return TargetFound;
} // end of SMPInstr::BuildJumpRTL()
// Add to the stack pointer to deallocate stack space, e.g. for a pop instruction.
void SMPInstr::AddToStackPointer(uval_t delta) {
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
op_t StackOp = InitOp, DeltaOp = InitOp;
StackOp.type = o_reg;
StackOp.reg = R_sp;
DeltaOp.type = o_imm;
DeltaOp.value = delta;
TempRT->SetLeftOperand(StackOp); // ESP := RightRT
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(StackOp); // ESP + delta
RightRT->SetOperator(SMP_ADD);
RightRT->SetRightOperand(DeltaOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
return;
} // end of SMPInstr::AddToStackPointer()
// Subtract from the stack pointer to allocate stack space, e.g. for a push instruction.
void SMPInstr::SubFromStackPointer(uval_t delta) {
SMPRegTransfer *TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
SMPRegTransfer *RightRT = new SMPRegTransfer;
RightRT->SetParentInst(this);
op_t StackOp = InitOp, DeltaOp = InitOp;
StackOp.type = o_reg;
StackOp.reg = R_sp;
DeltaOp.type = o_imm;
DeltaOp.value = delta;
TempRT->SetLeftOperand(StackOp); // ESP := RightRT
TempRT->SetOperator(SMP_ASSIGN);
RightRT->SetLeftOperand(StackOp); // ESP - delta
RightRT->SetOperator(SMP_SUBTRACT);
RightRT->SetRightOperand(DeltaOp);
TempRT->SetRightTree(RightRT);
this->RTL.push_back(TempRT);
return;
} // end of SMPInstr::SubFromStackPointer()
#define SMP_FIRST_POP_FLAGS NN_popfw
#define SMP_LAST_POP_FLAGS NN_popfq
#define SMP_FIRST_POP_ALL NN_popaw
#define SMP_LAST_POP_ALL NN_popaq
// Build the RTL for a pop instruction
bool SMPInstr::BuildPopRTL(void) {
size_t OpNum, OpSize;
bool DestFound = false;
SMPRegTransfer *TempRT = NULL;
op_t StackOp = InitOp, FlagsOp = InitOp;
StackOp.type = o_displ;
StackOp.reg = R_sp;
// StackOp.addr = 0; // [ESP+0]
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
// Handle special cases first.
if ((SMP_FIRST_POP_FLAGS <= this->SMPcmd.itype) && (SMP_LAST_POP_FLAGS >= this->SMPcmd.itype)) {
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(FlagsOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// Now create the stack pointer increment effect.
this->AddToStackPointer(4);
return true;
}
if ((SMP_FIRST_POP_ALL <= this->SMPcmd.itype) && (SMP_LAST_POP_ALL >= this->SMPcmd.itype)) {
// We pop off 7 registers from the 8 that were pushed on the stack.
// The pushed stack pointer is ignored. Instead, the stack pointer value is
// adjusted at the end, per the Intel instruction manuals.
op_t RegOp = InitOp;
RegOp.type = o_reg;
// EDI comes from [ESP+0]
RegOp.reg = R_di;
StackOp.addr = 0; // [ESP+0]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// ESI comes from [ESP+4]
RegOp.reg = R_si;
StackOp.addr = 4; // [ESP+4]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// EBP comes from [ESP+8]
RegOp.reg = R_bp;
StackOp.addr = 8; // [ESP+8]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// Skip over saved ESP at [ESP+12]
// EBX comes from [ESP+16]
RegOp.reg = R_bx;
StackOp.addr = 16; // [ESP+16]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// EDX comes from [ESP+20]
RegOp.reg = R_dx;
StackOp.addr = 20; // [ESP+20]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// ECX comes from [ESP+24]
RegOp.reg = R_cx;
StackOp.addr = 24; // [ESP+24]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// EAX comes from [ESP+28]
RegOp.reg = R_ax;
StackOp.addr = 28; // [ESP+28]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// Now create the stack pointer increment effect.
this->AddToStackPointer(32);
return true;
} // end for "pop all" instructions
// If we reach this point, we have a simple POP instruction.
for (OpNum = 0; !DestFound && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & DefMacros[OpNum]) { // DEF
if (MDKnownOperandType(TempOp)) {
DestFound = true;
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetLeftOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
StackOp.dtyp = TempOp.dtyp; // size of transfer
TempRT->SetRightOperand(StackOp);
this->RTL.push_back(TempRT);
// Now create the stack pointer increment effect.
OpSize = GetOpDataSize(TempOp);
this->AddToStackPointer((uval_t) OpSize);
}
}
} // end for (OpNum = 0; ...)
#if SMP_DEBUG_BUILD_RTL
if (!DestFound) {
SMP_msg("ERROR: Could not find pop operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
#endif
return DestFound;
} // end of SMPInstr::BuildPopRTL()
#define SMP_FIRST_PUSH_FLAGS NN_pushfw
#define SMP_LAST_PUSH_FLAGS NN_pushfq
#define SMP_FIRST_PUSH_ALL NN_pushaw
#define SMP_LAST_PUSH_ALL NN_pushaq
// Build the RTL for a push instruction
bool SMPInstr::BuildPushRTL(void) {
size_t OpNum, OpSize;
bool SourceFound = false;
SMPRegTransfer *TempRT = NULL;
op_t StackOp = InitOp, FlagsOp = InitOp;
StackOp.type = o_displ;
StackOp.reg = R_sp;
StackOp.addr = (ea_t) -4; // [ESP-4]
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
// Handle special cases first.
if ((SMP_FIRST_PUSH_FLAGS <= this->SMPcmd.itype) && (SMP_LAST_PUSH_FLAGS >= this->SMPcmd.itype)) {
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(FlagsOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
// Now create the stack pointer increment effect.
this->SubFromStackPointer(4);
return true;
}
if ((SMP_FIRST_PUSH_ALL <= this->SMPcmd.itype) && (SMP_LAST_PUSH_ALL >= this->SMPcmd.itype)) {
op_t RegOp = InitOp;
RegOp.type = o_reg;
// EDI goes to [ESP-32]
RegOp.reg = R_di;
StackOp.addr = (ea_t) -32; // [ESP-32]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// ESI goes to [ESP-28]
RegOp.reg = R_si;
StackOp.addr = (ea_t) -28; // [ESP-28]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// EBP goes to [ESP-24]
RegOp.reg = R_bp;
StackOp.addr = (ea_t) -24; // [ESP-24]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// ESP goes to [ESP-20]
RegOp.reg = R_sp;
StackOp.addr = (ea_t) -20; // [ESP-20]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// EBX goes to [ESP-16]
RegOp.reg = R_bx;
StackOp.addr = (ea_t) -16; // [ESP-16]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// EDX goes to [ESP-12]
RegOp.reg = R_dx;
StackOp.addr = (ea_t) -12; // [ESP-12]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// ECX goes to [ESP-8]
RegOp.reg = R_cx;
StackOp.addr = (ea_t) -8; // [ESP-8]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// EAX goes to [ESP-4]
RegOp.reg = R_ax;
StackOp.addr = (ea_t) -4; // [ESP-4]
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(RegOp);
TempRT->SetOperator(SMP_ASSIGN);
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// Now create the stack pointer increment effect.
this->SubFromStackPointer(32);
return true;
} // end for "pop all" instructions
// If we reach this point, we have a simple PUSH instruction.
for (OpNum = 0; !SourceFound && (OpNum < UA_MAXOP); ++OpNum) {
op_t TempOp = this->SMPcmd.Operands[OpNum];
if (this->features & UseMacros[OpNum]) { // USE
if (MDKnownOperandType(TempOp)) {
SourceFound = true;
OpSize = GetOpDataSize(TempOp);
TempRT = new SMPRegTransfer;
TempRT->SetParentInst(this);
TempRT->SetRightOperand(TempOp);
TempRT->SetOperator(SMP_ASSIGN);
StackOp.dtyp = TempOp.dtyp; // size of transfer
StackOp.addr = (ea_t) (-((signed int) OpSize));
TempRT->SetLeftOperand(StackOp);
this->RTL.push_back(TempRT);
TempRT = NULL;
// Now create the stack pointer increment effect.
this->SubFromStackPointer((uval_t) OpSize);
#if 0
this->RTL.Dump();
#endif
}
}
} // end for (OpNum = 0; ...)
#if SMP_DEBUG_BUILD_RTL
if (!SourceFound) {
SMP_msg("ERROR: Could not find push operand at %x for %s\n", this->GetAddr(), DisAsmText.GetDisAsm(this->GetAddr()));
}
#endif
return SourceFound;
} // end of SMPInstr::BuildPushRTL()
// Build RTL trees from the SMPcmd info.
bool SMPInstr::BuildRTL(void) {
op_t FlagsOp = InitOp;
FlagsOp.type = o_reg;
FlagsOp.reg = X86_FLAGS_REG;
SMPRegTransfer *NopRT = NULL; // no-op register transfer
// We don't want to explicitly represent the various no-ops except as NULL operations.
// E.g. mov esi,esi should not generate DEF and USE of esi, because esi does not change.
if (this->IsNop()) {
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
}
switch (this->SMPcmd.itype) {
case NN_aaa: // ASCII Adjust after Addition
case NN_aad: // ASCII Adjust AX before Division
case NN_aam: // ASCII Adjust AX after Multiply
case NN_aas: // ASCII Adjust AL after Subtraction
return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_adc: // Add with Carry
#if SMP_BUILD_SPECIAL_ADC_SBB_RTL
return this->BuildBinaryPlusFlagsRTL(SMP_ADD_CARRY);
#else
return this->BuildBinaryRTL(SMP_ADD_CARRY);
#endif
case NN_add: // Add
return this->BuildBinaryRTL(SMP_ADD);
case NN_and: // Logical AND
return this->BuildBinaryRTL(SMP_BITWISE_AND);
case NN_arpl: // Adjust RPL Field of Selector
return this->BuildBinaryRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_bound: // Check Array Index Against Bounds
return this->BuildGuardedSignalRTL(SMP_SIGNAL);
case NN_bsf: // Bit Scan Forward
case NN_bsr: // Bit Scan Reverse
return this->BuildUnary2OpndRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_bt: // Bit Test
return this->BuildFlagsDestBinaryRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_btc: // Bit Test and Complement
case NN_btr: // Bit Test and Reset
case NN_bts: // Bit Test and Set
// Has effects on both the carry flag and the first operand
this->RTL.ExtraKills.push_back(FlagsOp);
return this->BuildBinaryRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_call: // Call Procedure
case NN_callfi: // Indirect Call Far Procedure
case NN_callni: // Indirect Call Near Procedure
return this->BuildCallRTL();
case NN_cbw: // AL -> AX (with sign)
case NN_cwde: // AX -> EAX (with sign)
case NN_cdqe: // EAX -> RAX (with sign)
return this->BuildUnaryRTL(SMP_SIGN_EXTEND);
case NN_clc: // Clear Carry Flag
case NN_cld: // Clear Direction Flag
return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_cli: // Clear Interrupt Flag
case NN_clts: // Clear Task-Switched Flag in CR0
// We don't track the interrupt flag or the special registers,
// so we can just consider these to be no-ops.
// NOTE: Shouldn't we killthe EFLAGS register on NN_cli ??!!??!!
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_cmc: // Complement Carry Flag
return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_cmp: // Compare Two Operands
return this->BuildFlagsDestBinaryRTL(SMP_S_COMPARE);
case NN_cmps: // Compare Strings
// Why do we no longer use BuildCompareStringRTL()? ****!!!!**** Test it!
return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE);
case NN_cwd: // AX -> DX:AX (with sign)
case NN_cdq: // EAX -> EDX:EAX (with sign)
case NN_cqo: // RAX -> RDX:RAX (with sign)
return this->BuildUnary2OpndRTL(SMP_SIGN_EXTEND);
case NN_daa: // Decimal Adjust AL after Addition
case NN_das: // Decimal Adjust AL after Subtraction
return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_dec: // Decrement by 1
return this->BuildUnaryRTL(SMP_DECREMENT);
case NN_div: // Unsigned Divide
return this->BuildMultiplyDivideRTL(SMP_U_DIVIDE);
case NN_enterw: // Make Stack Frame for Procedure Parameters
case NN_enter: // Make Stack Frame for Procedure Parameters
case NN_enterd: // Make Stack Frame for Procedure Parameters
case NN_enterq: // Make Stack Frame for Procedure Parameters
return this->BuildEnterRTL();
case NN_hlt: // Halt
// Treat as a no-op
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_idiv: // Signed Divide
return this->BuildMultiplyDivideRTL(SMP_S_DIVIDE);
case NN_imul: // Signed Multiply
return this->BuildMultiplyDivideRTL(SMP_S_MULTIPLY);
case NN_in: // Input from Port
return this->BuildUnary2OpndRTL(SMP_INPUT);
case NN_inc: // Increment by 1
return this->BuildUnaryRTL(SMP_INCREMENT);
case NN_ins: // Input Byte(s) from Port to String
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_int: // Call to Interrupt Procedure
case NN_into: // Call to Interrupt Procedure if Overflow Flag = 1
case NN_int3: // Trap to Debugger
return this->BuildCallRTL();
case NN_iretw: // Interrupt Return
case NN_iret: // Interrupt Return
case NN_iretd: // Interrupt Return (use32)
case NN_iretq: // Interrupt Return (use64)
return this->BuildReturnRTL();
case NN_ja: // Jump if Above (CF=0 & ZF=0)
case NN_jae: // Jump if Above or Equal (CF=0)
case NN_jb: // Jump if Below (CF=1)
case NN_jbe: // Jump if Below or Equal (CF=1 | ZF=1)
case NN_jc: // Jump if Carry (CF=1)
return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_jcxz: // Jump if CX is 0
case NN_jecxz: // Jump if ECX is 0
case NN_jrcxz: // Jump if RCX is 0
return this->BuildJumpRTL(SMP_EQUAL); // special case in BuildJumpRTL()
case NN_je: // Jump if Equal (ZF=1)
return this->BuildJumpRTL(SMP_EQUAL);
case NN_jg: // Jump if Greater (ZF=0 & SF=OF)
return this->BuildJumpRTL(SMP_GREATER_THAN);
case NN_jge: // Jump if Greater or Equal (SF=OF)
return this->BuildJumpRTL(SMP_GREATER_EQUAL);
case NN_jl: // Jump if Less (SF!=OF)
return this->BuildJumpRTL(SMP_LESS_THAN);
case NN_jle: // Jump if Less or Equal (ZF=1 | SF!=OF)
return this->BuildJumpRTL(SMP_LESS_EQUAL);
case NN_jna: // Jump if Not Above (CF=1 | ZF=1)
case NN_jnae: // Jump if Not Above or Equal (CF=1)
case NN_jnb: // Jump if Not Below (CF=0)
case NN_jnbe: // Jump if Not Below or Equal (CF=0 & ZF=0) a.k.a. ja
case NN_jnc: // Jump if Not Carry (CF=0)
return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_jne: // Jump if Not Equal (ZF=0)
return this->BuildJumpRTL(SMP_NOT_EQUAL);
case NN_jng: // Jump if Not Greater (ZF=1 | SF!=OF) a.k.a. jle
return this->BuildJumpRTL(SMP_LESS_EQUAL);
case NN_jnge: // Jump if Not Greater or Equal (SF != OF) **
return this->BuildJumpRTL(SMP_LESS_THAN);
case NN_jnl: // Jump if Not Less (SF=OF) a.k.a. jge
return this->BuildJumpRTL(SMP_GREATER_EQUAL);
case NN_jnle: // Jump if Not Less or Equal (ZF=0 & SF=OF) a.k.a. jg
return this->BuildJumpRTL(SMP_GREATER_THAN);
case NN_jno: // Jump if Not Overflow (OF=0)
case NN_jnp: // Jump if Not Parity (PF=0)
case NN_jns: // Jump if Not Sign (SF=0)
return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_jnz: // Jump if Not Zero (ZF=0) a.k.a. jne
return this->BuildJumpRTL(SMP_NOT_EQUAL);
case NN_jo: // Jump if Overflow (OF=1)
case NN_jp: // Jump if Parity (PF=1)
case NN_jpe: // Jump if Parity Even (PF=1)
case NN_jpo: // Jump if Parity Odd (PF=0)
case NN_js: // Jump if Sign (SF=1)
return this->BuildJumpRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_jz: // Jump if Zero (ZF=1)
return this->BuildJumpRTL(SMP_EQUAL);
case NN_jmp: // Jump
case NN_jmpfi: // Indirect Far Jump
case NN_jmpni: // Indirect Near Jump
case NN_jmpshort: // Jump Short (not used)
return this->BuildJumpRTL(SMP_NULL_OPERATOR);
case NN_lahf: // Load Flags into AH Register
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
case NN_lar: // Load Access Right Byte
return false;
break;
case NN_lea: // Load Effective Address
return this->BuildLeaRTL();
break;
case NN_leavew: // High Level Procedure Exit
case NN_leave: // High Level Procedure Exit
case NN_leaved: // High Level Procedure Exit
case NN_leaveq: // High Level Procedure Exit
return this->BuildLeaveRTL();
break;
case NN_lgdt: // Load Global Descriptor Table Register
case NN_lidt: // Load Interrupt Descriptor Table Register
return false;
break;
case NN_lgs: // Load Full Pointer to GS:xx
case NN_lss: // Load Full Pointer to SS:xx
case NN_lds: // Load Full Pointer to DS:xx
case NN_les: // Load Full Pointer to ES:xx
case NN_lfs: // Load Full Pointer to FS:xx
// These instructions differ from NN_lea only in setting
// a segment register in addition to a pointer. We are
// not yet tracking segment registers.
return this->BuildLeaRTL();
break;
case NN_lldt: // Load Local Descriptor Table Register
case NN_lmsw: // Load Machine Status Word
case NN_lock: // Assert LOCK# Signal Prefix
return false;
break;
case NN_lods: // Load String
return this->BuildLoadStringRTL();
break;
case NN_loopw: // Loop while ECX != 0
case NN_loop: // Loop while CX != 0
case NN_loopd: // Loop while ECX != 0
case NN_loopq: // Loop while RCX != 0
case NN_loopwe: // Loop while CX != 0 and ZF=1
case NN_loope: // Loop while rCX != 0 and ZF=1
case NN_loopde: // Loop while ECX != 0 and ZF=1
case NN_loopqe: // Loop while RCX != 0 and ZF=1
case NN_loopwne: // Loop while CX != 0 and ZF=0
case NN_loopne: // Loop while rCX != 0 and ZF=0
case NN_loopdne: // Loop while ECX != 0 and ZF=0
case NN_loopqne: // Loop while RCX != 0 and ZF=0
return this->BuildLoopRTL(SMP_NULL_OPERATOR);
break;
case NN_lsl: // Load Segment Limit
case NN_ltr: // Load Task Register
return false;
break;
case NN_mov: // Move Data
case NN_movsp: // Move to/from Special Registers
case NN_movs: // Move Byte(s) from String to String
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
case NN_movsx: // Move with Sign-Extend
return this->BuildUnary2OpndRTL(SMP_SIGN_EXTEND);
case NN_movzx: // Move with Zero-Extend
return this->BuildUnary2OpndRTL(SMP_ZERO_EXTEND);
case NN_mul: // Unsigned Multiplication of AL or AX
return this->BuildMultiplyDivideRTL(SMP_U_MULTIPLY);
case NN_neg: // Two's Complement Negation
return this->BuildUnaryRTL(SMP_NEGATE);
case NN_nop: // No Operation
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_not: // One's Complement Negation
return this->BuildUnaryRTL(SMP_BITWISE_NOT);
case NN_or: // Logical Inclusive OR
return this->BuildBinaryRTL(SMP_BITWISE_OR);
case NN_out: // Output to Port
return this->BuildUnary2OpndRTL(SMP_OUTPUT);
case NN_outs: // Output Byte(s) to Port
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_pop: // Pop a word from the Stack
case NN_popaw: // Pop all General Registers
case NN_popa: // Pop all General Registers
case NN_popad: // Pop all General Registers (use32)
case NN_popaq: // Pop all General Registers (use64)
case NN_popfw: // Pop Stack into Flags Register
case NN_popf: // Pop Stack into Flags Register
case NN_popfd: // Pop Stack into Eflags Register
case NN_popfq: // Pop Stack into Rflags Register
return this->BuildPopRTL();
case NN_push: // Push Operand onto the Stack
case NN_pushaw: // Push all General Registers
case NN_pusha: // Push all General Registers
case NN_pushad: // Push all General Registers (use32)
case NN_pushaq: // Push all General Registers (use64)
case NN_pushfw: // Push Flags Register onto the Stack
case NN_pushf: // Push Flags Register onto the Stack
case NN_pushfd: // Push Flags Register onto the Stack (use32)
case NN_pushfq: // Push Flags Register onto the Stack (use64)
return this->BuildPushRTL();
case NN_rcl: // Rotate Through Carry Left
return this->BuildBinaryPlusFlagsRTL(SMP_ROTATE_LEFT_CARRY);
case NN_rcr: // Rotate Through Carry Right
return this->BuildBinaryPlusFlagsRTL(SMP_ROTATE_RIGHT_CARRY);
case NN_rol: // Rotate Left
return this->BuildBinaryRTL(SMP_ROTATE_LEFT);
case NN_ror: // Rotate Right
return this->BuildBinaryRTL(SMP_ROTATE_RIGHT);
case NN_rep: // Repeat String Operation
case NN_repe: // Repeat String Operation while ZF=1
case NN_repne: // Repeat String Operation while ZF=0
return false;
break;
case NN_retn: // Return Near from Procedure
case NN_retf: // Return Far from Procedure
return this->BuildReturnRTL();
case NN_sahf: // Store AH into Flags Register
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
case NN_sal: // Shift Arithmetic Left
return this->BuildBinaryRTL(SMP_S_LEFT_SHIFT);
case NN_sar: // Shift Arithmetic Right
return this->BuildBinaryRTL(SMP_S_RIGHT_SHIFT);
case NN_shl: // Shift Logical Left
return this->BuildBinaryRTL(SMP_U_LEFT_SHIFT);
case NN_shr: // Shift Logical Right
return this->BuildBinaryRTL(SMP_U_RIGHT_SHIFT);
case NN_sbb: // Integer Subtraction with Borrow
#if SMP_BUILD_SPECIAL_ADC_SBB_RTL
return this->BuildBinaryPlusFlagsRTL(SMP_SUBTRACT_BORROW);
#else
return this->BuildBinaryRTL(SMP_SUBTRACT_BORROW);
#endif
case NN_scas: // Scan String
return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE);
case NN_seta: // Set Byte if Above (CF=0 & ZF=0)
case NN_setae: // Set Byte if Above or Equal (CF=0)
case NN_setb: // Set Byte if Below (CF=1)
case NN_setbe: // Set Byte if Below or Equal (CF=1 | ZF=1)
case NN_setc: // Set Byte if Carry (CF=1)
case NN_sete: // Set Byte if Equal (ZF=1)
case NN_setg: // Set Byte if Greater (ZF=0 & SF=OF)
case NN_setge: // Set Byte if Greater or Equal (SF=OF)
case NN_setl: // Set Byte if Less (SF!=OF)
case NN_setle: // Set Byte if Less or Equal (ZF=1 | SF!=OF)
case NN_setna: // Set Byte if Not Above (CF=1 | ZF=1)
case NN_setnae: // Set Byte if Not Above or Equal (CF=1)
case NN_setnb: // Set Byte if Not Below (CF=0)
case NN_setnbe: // Set Byte if Not Below or Equal (CF=0 & ZF=0)
case NN_setnc: // Set Byte if Not Carry (CF=0)
case NN_setne: // Set Byte if Not Equal (ZF=0)
case NN_setng: // Set Byte if Not Greater (ZF=1 | SF!=OF)
case NN_setnge: // Set Byte if Not Greater or Equal (ZF=1)
case NN_setnl: // Set Byte if Not Less (SF=OF)
case NN_setnle: // Set Byte if Not Less or Equal (ZF=0 & SF=OF)
case NN_setno: // Set Byte if Not Overflow (OF=0)
case NN_setnp: // Set Byte if Not Parity (PF=0)
case NN_setns: // Set Byte if Not Sign (SF=0)
case NN_setnz: // Set Byte if Not Zero (ZF=0)
case NN_seto: // Set Byte if Overflow (OF=1)
case NN_setp: // Set Byte if Parity (PF=1)
case NN_setpe: // Set Byte if Parity Even (PF=1)
case NN_setpo: // Set Byte if Parity Odd (PF=0)
case NN_sets: // Set Byte if Sign (SF=1)
case NN_setz: // Set Byte if Zero (ZF=1)
// Destination always get set to NUMERIC 0 or 1, depending on
// the condition and the relevant flags bits. Best way to model
// this in an RTL is to perform an unspecified unary NUMERIC
// operation on the flags register and assign the result to the
// destination operand, making it always NUMERIC.
return this->BuildUnary2OpndRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_sgdt: // Store Global Descriptor Table Register
case NN_sidt: // Store Interrupt Descriptor Table Register
return false;
break;
case NN_shld: // Double Precision Shift Left
return this->BuildDoubleShiftRTL(SMP_U_LEFT_SHIFT);
case NN_shrd: // Double Precision Shift Right
return this->BuildDoubleShiftRTL(SMP_U_RIGHT_SHIFT);
case NN_sldt: // Store Local Descriptor Table Register
case NN_smsw: // Store Machine Status Word
return false;
break;
case NN_stc: // Set Carry Flag
case NN_std: // Set Direction Flag
return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_sti: // Set Interrupt Flag
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_stos: // Store String
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
case NN_str: // Store Task Register
return false;
break;
case NN_sub: // Integer Subtraction
return this->BuildBinaryRTL(SMP_SUBTRACT);
case NN_test: // Logical Compare
return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE);
case NN_verr: // Verify a Segment for Reading
case NN_verw: // Verify a Segment for Writing
case NN_wait: // Wait until BUSY# Pin is Inactive (HIGH)
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
if (NN_wait != this->SMPcmd.itype)
this->RTL.ExtraKills.push_back(FlagsOp);
return true;
case NN_xchg: // Exchange Register/Memory with Register
return this->BuildExchangeRTL();
case NN_xlat: // Table Lookup Translation
return false;
break;
case NN_xor: // Logical Exclusive OR
return this->BuildBinaryRTL(SMP_BITWISE_XOR);
//
// 486 instructions
//
case NN_cmpxchg: // Compare and Exchange
return this->BuildCompareExchangeRTL();
case NN_bswap: // Swap bits in EAX
return this->BuildUnaryRTL(SMP_UNARY_NUMERIC_OPERATION);
case NN_xadd: // t<-dest; dest<-src+dest; src<-t
return this->BuildExchangeAddRTL();
case NN_invd: // Invalidate Data Cache
case NN_wbinvd: // Invalidate Data Cache (write changes)
case NN_invlpg: // Invalidate TLB entry
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
//
// Pentium instructions
//
case NN_rdmsr: // Read Machine Status Register
return this->BuildOptType8RTL();
case NN_wrmsr: // Write Machine Status Register
return false;
break;
case NN_cpuid: // Get CPU ID
return this->BuildOptType8RTL();
case NN_cmpxchg8b: // Compare and Exchange Eight Bytes
return false;
break;
case NN_rdtsc: // Read Time Stamp Counter
return this->BuildOptType8RTL();
case NN_rsm: // Resume from System Management Mode
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
//
// Pentium Pro instructions
//
case NN_cmova: // Move if Above (CF=0 & ZF=0)
case NN_cmovb: // Move if Below (CF=1)
case NN_cmovbe: // Move if Below or Equal (CF=1 | ZF=1)
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_cmovg: // Move if Greater (ZF=0 & SF=OF)
return this->BuildMoveRTL(SMP_GREATER_THAN);
case NN_cmovge: // Move if Greater or Equal (SF=OF)
return this->BuildMoveRTL(SMP_GREATER_EQUAL);
case NN_cmovl: // Move if Less (SF!=OF)
return this->BuildMoveRTL(SMP_LESS_THAN);
case NN_cmovle: // Move if Less or Equal (ZF=1 | SF!=OF)
return this->BuildMoveRTL(SMP_LESS_EQUAL);
case NN_cmovnb: // Move if Not Below (CF=0)
case NN_cmovno: // Move if Not Overflow (OF=0)
case NN_cmovnp: // Move if Not Parity (PF=0)
case NN_cmovns: // Move if Not Sign (SF=0)
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_cmovnz: // Move if Not Zero (ZF=0)
return this->BuildMoveRTL(SMP_NOT_EQUAL);
case NN_cmovo: // Move if Overflow (OF=1)
case NN_cmovp: // Move if Parity (PF=1)
case NN_cmovs: // Move if Sign (SF=1)
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_cmovz: // Move if Zero (ZF=1)
return this->BuildMoveRTL(SMP_EQUAL);
case NN_fcmovb: // Floating Move if Below
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_fcmove: // Floating Move if Equal
return this->BuildMoveRTL(SMP_EQUAL);
case NN_fcmovbe: // Floating Move if Below or Equal
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_fcmovu: // Floating Move if Unordered
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_fcmovnb: // Floating Move if Not Below
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_fcmovne: // Floating Move if Not Equal
return this->BuildMoveRTL(SMP_NOT_EQUAL);
case NN_fcmovnbe: // Floating Move if Not Below or Equal
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_fcmovnu: // Floating Move if Not Unordered
return this->BuildMoveRTL(SMP_BINARY_NUMERIC_OPERATION);
case NN_fcomi: // FP Compare: result in EFLAGS
case NN_fucomi: // FP Unordered Compare: result in EFLAGS
case NN_fcomip: // FP Compare: result in EFLAGS: pop stack
case NN_fucomip: // FP Unordered Compare: result in EFLAGS: pop stack
return this->BuildFlagsDestBinaryRTL(SMP_S_COMPARE);
break;
case NN_rdpmc: // Read Performance Monitor Counter
return this->BuildOptType8RTL();
//
// FPP instructions
//
case NN_fld: // Load Real
case NN_fst: // Store Real
case NN_fstp: // Store Real and Pop
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
case NN_fxch: // Exchange Registers
// FP registers remain NUMERIC anyway, so this is a no-op to our type system.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_fild: // Load Integer
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
case NN_fist: // Store Integer
case NN_fistp: // Store Integer and Pop
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
case NN_fbld: // Load BCD
case NN_fbstp: // Store BCD and Pop
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
case NN_fadd: // Add Real
case NN_faddp: // Add Real and Pop
case NN_fiadd: // Add Integer
case NN_fsub: // Subtract Real
case NN_fsubp: // Subtract Real and Pop
case NN_fisub: // Subtract Integer
case NN_fsubr: // Subtract Real Reversed
case NN_fsubrp: // Subtract Real Reversed and Pop
case NN_fisubr: // Subtract Integer Reversed
case NN_fmul: // Multiply Real
case NN_fmulp: // Multiply Real and Pop
case NN_fimul: // Multiply Integer
case NN_fdiv: // Divide Real
case NN_fdivp: // Divide Real and Pop
case NN_fidiv: // Divide Integer
case NN_fdivr: // Divide Real Reversed
case NN_fdivrp: // Divide Real Reversed and Pop
case NN_fidivr: // Divide Integer Reversed
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC, true);
case NN_fsqrt: // Square Root
case NN_fscale: // Scale: st(0) <- st(0) * 2^st(1)
case NN_fprem: // Partial Remainder
case NN_frndint: // Round to Integer
case NN_fxtract: // Extract exponent and significand
case NN_fabs: // Absolute value
case NN_fchs: // Change Sign
return this->BuildUnaryRTL(SMP_UNARY_FLOATING_ARITHMETIC);
case NN_fcom: // Compare Real
case NN_fcomp: // Compare Real and Pop
case NN_fcompp: // Compare Real and Pop Twice
case NN_ficom: // Compare Integer
case NN_ficomp: // Compare Integer and Pop
case NN_ftst: // Test
case NN_fxam: // Examine
// Floating comparison instructions use FP reg stack locations
// as sources and set only the FP flags. All of these are numeric
// type and we don't track any of them, so all such instructions
// can be considered to be no-ops.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_fptan: // Partial tangent
case NN_fpatan: // Partial arctangent
case NN_f2xm1: // 2^x - 1
case NN_fyl2x: // Y * lg2(X)
case NN_fyl2xp1: // Y * lg2(X+1)
// We can consider it a unary operation when both arguments come
// off the floating point register stack, unless we ever start
// modeling the different locations in the FP register stack.
return this->BuildUnaryRTL(SMP_UNARY_FLOATING_ARITHMETIC);
case NN_fldz: // Load +0.0
case NN_fld1: // Load +1.0
case NN_fldpi: // Load PI=3.14...
case NN_fldl2t: // Load lg2(10)
case NN_fldl2e: // Load lg2(e)
case NN_fldlg2: // Load lg10(2)
case NN_fldln2: // Load ln(2)
case NN_finit: // Initialize Processor
case NN_fninit: // Initialize Processor (no wait)
case NN_fsetpm: // Set Protected Mode
case NN_fldcw: // Load Control Word
case NN_fstcw: // Store Control Word
case NN_fnstcw: // Store Control Word (no wait)
case NN_fstsw: // Store Status Word
case NN_fnstsw: // Store Status Word (no wait)
case NN_fclex: // Clear Exceptions
case NN_fnclex: // Clear Exceptions (no wait)
// Floating point stack and control word and flags operations
// with no memory operands are no-ops to us.
// NOTE: We might want to deal with the memory operands in some
// of these opcodes later.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_fstenv: // Store Environment
case NN_fnstenv: // Store Environment (no wait)
case NN_fldenv: // Load Environment
case NN_fsave: // Save State
case NN_fnsave: // Save State (no wait)
case NN_frstor: // Restore State
case NN_fincstp: // Increment Stack Pointer
case NN_fdecstp: // Decrement Stack Pointer
case NN_ffree: // Free Register
// Floating point stack and control word and flags operations
// with no memory operands are no-ops to us.
// NOTE: We might want to deal with the memory operands in some
// of these opcodes later.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_fnop: // No Operation
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_feni: // (8087 only)
case NN_fneni: // (no wait) (8087 only)
case NN_fdisi: // (8087 only)
case NN_fndisi: // (no wait) (8087 only)
return false;
break;
//
// 80387 instructions
//
case NN_fprem1: // Partial Remainder ( < half )
case NN_fsincos: // t<-cos(st); st<-sin(st); push t
case NN_fsin: // Sine
case NN_fcos: // Cosine
case NN_fucom: // Compare Unordered Real
case NN_fucomp: // Compare Unordered Real and Pop
case NN_fucompp: // Compare Unordered Real and Pop Twice
// Floating point stack and control word and flags operations
// with no memory operands are no-ops to us.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
//
// Instructions added 28.02.96
//
case NN_setalc: // Set AL to Carry Flag
case NN_svdc: // Save Register and Descriptor
case NN_rsdc: // Restore Register and Descriptor
case NN_svldt: // Save LDTR and Descriptor
case NN_rsldt: // Restore LDTR and Descriptor
case NN_svts: // Save TR and Descriptor
case NN_rsts: // Restore TR and Descriptor
case NN_icebp: // ICE Break Point
case NN_loadall: // Load the entire CPU state from ES:EDI
return false;
break;
//
// MMX instructions
//
case NN_emms: // Empty MMX state
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
break;
case NN_movd: // Move 32 bits
case NN_movq: // Move 64 bits
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_packsswb: // Pack with Signed Saturation (Word->Byte)
case NN_packssdw: // Pack with Signed Saturation (Dword->Word)
return this->BuildBinaryRTL(SMP_PACK_S);
break;
case NN_packuswb: // Pack with Unsigned Saturation (Word->Byte)
return this->BuildBinaryRTL(SMP_PACK_U);
break;
case NN_paddb: // Packed Add Byte
case NN_paddw: // Packed Add Word
case NN_paddd: // Packed Add Dword
case NN_paddsb: // Packed Add with Saturation (Byte)
case NN_paddsw: // Packed Add with Saturation (Word)
case NN_paddusb: // Packed Add Unsigned with Saturation (Byte)
case NN_paddusw: // Packed Add Unsigned with Saturation (Word)
return this->BuildBinaryRTL(SMP_ADD);
break;
case NN_pand: // Bitwise Logical And
return this->BuildBinaryRTL(SMP_BITWISE_AND);
break;
case NN_pandn: // Bitwise Logical And Not
return this->BuildBinaryRTL(SMP_BITWISE_AND_NOT);
break;
case NN_pcmpeqb: // Packed Compare for Equal (Byte)
case NN_pcmpeqw: // Packed Compare for Equal (Word)
case NN_pcmpeqd: // Packed Compare for Equal (Dword)
return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET);
break;
case NN_pcmpgtb: // Packed Compare for Greater Than (Byte)
case NN_pcmpgtw: // Packed Compare for Greater Than (Word)
case NN_pcmpgtd: // Packed Compare for Greater Than (Dword)
return this->BuildBinaryRTL(SMP_COMPARE_GT_AND_SET);
break;
case NN_pmaddwd: // Packed Multiply and Add
return this->BuildBinaryRTL(SMP_MULTIPLY_AND_ADD);
break;
case NN_pmulhw: // Packed Multiply High
case NN_pmullw: // Packed Multiply Low
return this->BuildBinaryRTL(SMP_U_MULTIPLY);
break;
case NN_por: // Bitwise Logical Or
return this->BuildBinaryRTL(SMP_BITWISE_OR);
break;
case NN_psllw: // Packed Shift Left Logical (Word)
case NN_pslld: // Packed Shift Left Logical (Dword)
case NN_psllq: // Packed Shift Left Logical (Qword)
return this->BuildBinaryRTL(SMP_U_LEFT_SHIFT);
break;
case NN_psraw: // Packed Shift Right Arithmetic (Word)
case NN_psrad: // Packed Shift Right Arithmetic (Dword)
return this->BuildBinaryRTL(SMP_S_RIGHT_SHIFT);
break;
case NN_psrlw: // Packed Shift Right Logical (Word)
case NN_psrld: // Packed Shift Right Logical (Dword)
case NN_psrlq: // Packed Shift Right Logical (Qword)
return this->BuildBinaryRTL(SMP_U_RIGHT_SHIFT);
break;
case NN_psubb: // Packed Subtract Byte
case NN_psubw: // Packed Subtract Word
case NN_psubd: // Packed Subtract Dword
return this->BuildBinaryRTL(SMP_SUBTRACT);
break;
case NN_psubsb: // Packed Subtract with Saturation (Byte)
case NN_psubsw: // Packed Subtract with Saturation (Word)
return this->BuildBinaryRTL(SMP_SUBTRACT);
break;
case NN_psubusb: // Packed Subtract Unsigned with Saturation (Byte)
case NN_psubusw: // Packed Subtract Unsigned with Saturation (Word)
return this->BuildBinaryRTL(SMP_SUBTRACT);
break;
case NN_punpckhbw: // Unpack High Packed Data (Byte->Word)
case NN_punpckhwd: // Unpack High Packed Data (Word->Dword)
case NN_punpckhdq: // Unpack High Packed Data (Dword->Qword)
case NN_punpcklbw: // Unpack Low Packed Data (Byte->Word)
case NN_punpcklwd: // Unpack Low Packed Data (Word->Dword)
case NN_punpckldq: // Unpack Low Packed Data (Dword->Qword)
return this->BuildBinaryRTL(SMP_INTERLEAVE);
break;
case NN_pxor: // Bitwise Logical Exclusive Or
return this->BuildBinaryRTL(SMP_BITWISE_XOR);
break;
//
// Undocumented Deschutes processor instructions
//
case NN_fxsave: // Fast save FP context
case NN_fxrstor: // Fast restore FP context
return false;
break;
// Pentium II instructions
case NN_sysenter: // Fast Transition to System Call Entry Point
case NN_sysexit: // Fast Transition from System Call Entry Point
return false;
break;
// 3DNow! instructions
case NN_pavgusb: // Packed 8-bit Unsigned Integer Averaging
return this->BuildBinaryRTL(SMP_AVERAGE_U);
break;
case NN_pfadd: // Packed Floating-Point Addition
return this->BuildBinaryRTL(SMP_ADD);
break;
case NN_pfsub: // Packed Floating-Point Subtraction
case NN_pfsubr: // Packed Floating-Point Reverse Subtraction !!!!****!!!! Fix this reversal if we are about the details
return this->BuildBinaryRTL(SMP_SUBTRACT);
break;
case NN_pfacc: // Packed Floating-Point Accumulate
return this->BuildBinaryRTL(SMP_ADD);
break;
case NN_pfcmpge: // Packed Floating-Point Comparison: Greater or Equal
return this->BuildBinaryRTL(SMP_COMPARE_GE_AND_SET);
break;
case NN_pfcmpgt: // Packed Floating-Point Comparison: Greater
return this->BuildBinaryRTL(SMP_COMPARE_GT_AND_SET);
break;
case NN_pfcmpeq: // Packed Floating-Point Comparison: Equal
return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET);
break;
case NN_pfmin: // Packed Floating-Point Minimum
return this->BuildBinaryRTL(SMP_MIN_S);
break;
case NN_pfmax: // Packed Floating-Point Maximum
return this->BuildBinaryRTL(SMP_MAX_S);
break;
case NN_pi2fd: // Packed 32-bit Integer to Floating-Point
return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP);
break;
case NN_pf2id: // Packed Floating-Point to 32-bit Integer
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_pfrcp: // Packed Floating-Point Reciprocal Approximation
case NN_pfrsqrt: // Packed Floating-Point Reciprocal Square Root Approximation
return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC);
break;
case NN_pfmul: // Packed Floating-Point Multiplication
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_pfrcpit1: // Packed Floating-Point Reciprocal First Iteration Step
case NN_pfrsqit1: // Packed Floating-Point Reciprocal Square Root First Iteration Step
case NN_pfrcpit2: // Packed Floating-Point Reciprocal Second Iteration Step
return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC);
break;
case NN_pmulhrw: // Packed Floating-Point 16-bit Integer Multiply with rounding
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_femms: // Faster entry/exit of the MMX or floating-point state
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
break;
case NN_prefetch: // Prefetch at least a 32-byte line into L1 data cache
case NN_prefetchw: // Prefetch processor cache line into L1 data cache (mark as modified)
// Prefetch opcodes are no-ops to us.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
// Pentium III instructions
case NN_addps: // Packed Single-FP Add
case NN_addss: // Scalar Single-FP Add
case NN_andnps: // Bitwise Logical And Not for Single-FP
case NN_andps: // Bitwise Logical And for Single-FP
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_cmpps: // Packed Single-FP Compare
case NN_cmpss: // Scalar Single-FP Compare
case NN_comiss: // Scalar Ordered Single-FP Compare and Set EFLAGS
return false;
break;
case NN_cvtpi2ps: // Packed signed INT32 to Packed Single-FP conversion
return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP);
break;
case NN_cvtps2pi: // Packed Single-FP to Packed INT32 conversion
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_cvtsi2ss: // Scalar signed INT32 to Single-FP conversion
return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP);
break;
case NN_cvtss2si: // Scalar Single-FP to signed INT32 conversion
case NN_cvttps2pi: // Packed Single-FP to Packed INT32 conversion (truncate)
case NN_cvttss2si: // Scalar Single-FP to signed INT32 conversion (truncate)
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_divps: // Packed Single-FP Divide
case NN_divss: // Scalar Single-FP Divide
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_ldmxcsr: // Load Streaming SIMD Extensions Technology Control/Status Register
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_maxps: // Packed Single-FP Maximum
case NN_maxss: // Scalar Single-FP Maximum
return this->BuildBinaryRTL(SMP_MAX_S);
break;
case NN_minps: // Packed Single-FP Minimum
case NN_minss: // Scalar Single-FP Minimum
return this->BuildBinaryRTL(SMP_MIN_S);
break;
case NN_movaps: // Move Aligned Four Packed Single-FP
case NN_movhlps: // Move High to Low Packed Single-FP
case NN_movhps: // Move High Packed Single-FP
case NN_movlhps: // Move Low to High Packed Single-FP
case NN_movlps: // Move Low Packed Single-FP
case NN_movmskps: // Move Mask to Register
case NN_movss: // Move Scalar Single-FP
case NN_movups: // Move Unaligned Four Packed Single-FP
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_mulps: // Packed Single-FP Multiply
case NN_mulss: // Scalar Single-FP Multiply
case NN_orps: // Bitwise Logical OR for Single-FP Data
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_rcpps: // Packed Single-FP Reciprocal
case NN_rcpss: // Scalar Single-FP Reciprocal
case NN_rsqrtps: // Packed Single-FP Square Root Reciprocal
case NN_rsqrtss: // Scalar Single-FP Square Root Reciprocal
return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC);
break;
case NN_shufps: // Shuffle Single-FP
return this->BuildBinaryRTL(SMP_SHUFFLE);
break;
case NN_sqrtps: // Packed Single-FP Square Root
case NN_sqrtss: // Scalar Single-FP Square Root
return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC);
break;
case NN_stmxcsr: // Store Streaming SIMD Extensions Technology Control/Status Register
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_subps: // Packed Single-FP Subtract
case NN_subss: // Scalar Single-FP Subtract
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_ucomiss: // Scalar Unordered Single-FP Compare and Set EFLAGS
return false;
break;
case NN_unpckhps: // Unpack High Packed Single-FP Data
case NN_unpcklps: // Unpack Low Packed Single-FP Data
return this->BuildBinaryRTL(SMP_INTERLEAVE);
break;
case NN_xorps: // Bitwise Logical XOR for Single-FP Data
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_pavgb: // Packed Average (Byte)
case NN_pavgw: // Packed Average (Word)
return this->BuildBinaryRTL(SMP_AVERAGE_U);
break;
case NN_pextrw: // Extract Word
return this->BuildBinaryPlusImmedRTL(SMP_EXTRACT_ZERO_EXTEND, SMP_CREATE_MASK);
break;
case NN_pinsrw: // Insert Word
return this->BuildBinaryPlusImmedRTL(SMP_BITWISE_AND, SMP_CREATE_MASK);
break;
case NN_pmaxsw: // Packed Signed Integer Word Maximum
return this->BuildBinaryRTL(SMP_MAX_S);
break;
case NN_pmaxub: // Packed Unsigned Integer Byte Maximum
return this->BuildBinaryRTL(SMP_MAX_U);
break;
case NN_pminsw: // Packed Signed Integer Word Minimum
return this->BuildBinaryRTL(SMP_MIN_S);
break;
case NN_pminub: // Packed Unsigned Integer Byte Minimum
return this->BuildBinaryRTL(SMP_MIN_U);
break;
case NN_pmovmskb: // Move Byte Mask to Integer
return this->BuildBinaryRTL(SMP_SHUFFLE);
break;
case NN_pmulhuw: // Packed Multiply High Unsigned
return this->BuildBinaryRTL(SMP_U_MULTIPLY);
break;
case NN_psadbw: // Packed Sum of Absolute Differences
return this->BuildBinaryRTL(SMP_SUM_OF_DIFFS);
break;
case NN_pshufw: // Packed Shuffle Word
return this->BuildBinaryRTL(SMP_SHUFFLE);
break;
case NN_maskmovq: // Byte Mask write
return false;
break;
case NN_movntps: // Move Aligned Four Packed Single-FP Non Temporal
case NN_movntq: // Move 64 Bits Non Temporal
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_prefetcht0: // Prefetch to all cache levels
case NN_prefetcht1: // Prefetch to all cache levels
case NN_prefetcht2: // Prefetch to L2 cache
case NN_prefetchnta: // Prefetch to L1 cache
case NN_sfence: // Store Fence
// Cache prefetch and store fence opcodes are no-ops to us.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
// Pentium III Pseudo instructions
case NN_cmpeqps: // Packed Single-FP Compare EQ
return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET);
break;
case NN_cmpltps: // Packed Single-FP Compare LT
return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET);
break;
case NN_cmpleps: // Packed Single-FP Compare LE
return this->BuildBinaryRTL(SMP_COMPARE_LE_AND_SET);
break;
case NN_cmpunordps: // Packed Single-FP Compare UNORD
return false;
break;
case NN_cmpneqps: // Packed Single-FP Compare NOT EQ
return this->BuildBinaryRTL(SMP_COMPARE_NE_AND_SET);
break;
case NN_cmpnltps: // Packed Single-FP Compare NOT LT
return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET);
break;
case NN_cmpnleps: // Packed Single-FP Compare NOT LE
return this->BuildBinaryRTL(SMP_COMPARE_GT_AND_SET);
break;
case NN_cmpordps: // Packed Single-FP Compare ORDERED
return false;
break;
case NN_cmpeqss: // Scalar Single-FP Compare EQ
return this->BuildBinaryRTL(SMP_COMPARE_EQ_AND_SET);
break;
case NN_cmpltss: // Scalar Single-FP Compare LT
return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET);
break;
case NN_cmpless: // Scalar Single-FP Compare LE
return this->BuildBinaryRTL(SMP_COMPARE_LE_AND_SET);
break;
case NN_cmpunordss: // Scalar Single-FP Compare UNORD
return false;
break;
case NN_cmpneqss: // Scalar Single-FP Compare NOT EQ
return this->BuildBinaryRTL(SMP_COMPARE_NE_AND_SET);
break;
case NN_cmpnltss: // Scalar Single-FP Compare NOT LT
return this->BuildBinaryRTL(SMP_COMPARE_LT_AND_SET);
break;
case NN_cmpnless: // Scalar Single-FP Compare NOT LE
return this->BuildBinaryRTL(SMP_COMPARE_LE_AND_SET);
break;
case NN_cmpordss: // Scalar Single-FP Compare ORDERED
return false;
break;
// AMD K7 instructions
case NN_pf2iw: // Packed Floating-Point to Integer with Sign Extend
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_pfnacc: // Packed Floating-Point Negative Accumulate
case NN_pfpnacc: // Packed Floating-Point Mixed Positive-Negative Accumulate
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_pi2fw: // Packed 16-bit Integer to Floating-Point
return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP);
break;
case NN_pswapd: // Packed Swap Double Word
return this->BuildExchangeRTL();
break;
// Undocumented FP instructions (thanks to norbert.juffa@adm.com)
case NN_fstp1: // Alias of Store Real and Pop
case NN_fcom2: // Alias of Compare Real
case NN_fcomp3: // Alias of Compare Real and Pop
case NN_fxch4: // Alias of Exchange Registers
case NN_fcomp5: // Alias of Compare Real and Pop
return false;
break;
case NN_ffreep: // Free Register and Pop; used in mplayer binary
// Floating point stack and control word and flags operations
// with no memory operands are no-ops to us.
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_fxch7: // Alias of Exchange Registers
case NN_fstp8: // Alias of Store Real and Pop
case NN_fstp9: // Alias of Store Real and Pop
return false;
break;
// Pentium 4 instructions
case NN_addpd: // Add Packed Double-Precision Floating-Point Values
case NN_addsd: // Add Scalar Double-Precision Floating-Point Values
case NN_andnpd: // Bitwise Logical AND NOT of Packed Double-Precision Floating-Point Values
case NN_andpd: // Bitwise Logical AND of Packed Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_clflush: // Flush Cache Line
case NN_cmppd: // Compare Packed Double-Precision Floating-Point Values
case NN_cmpsd: // Compare Scalar Double-Precision Floating-Point Values
case NN_comisd: // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS
return false;
break;
case NN_cvtdq2pd: // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values
case NN_cvtdq2ps: // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP);
break;
case NN_cvtpd2dq: // Convert Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
case NN_cvtpd2pi: // Convert Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
case NN_cvtpd2ps: // Convert Packed Double-Precision Floating-Point Values to Packed Single-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_cvtpi2pd: // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP);
break;
case NN_cvtps2dq: // Convert Packed Single-Precision Floating-Point Values to Packed Doubleword Integers
case NN_cvtps2pd: // Convert Packed Single-Precision Floating-Point Values to Packed Double-Precision Floating-Point Values
case NN_cvtsd2si: // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer
case NN_cvtsd2ss: // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_cvtsi2sd: // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value
return this->BuildBinaryRTL(SMP_CONVERT_INT_TO_FP);
break;
case NN_cvtss2sd: // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value
case NN_cvttpd2dq: // Convert With Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
case NN_cvttpd2pi: // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers
case NN_cvttps2dq: // Convert With Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers
case NN_cvttsd2si: // Convert with Truncation Scalar Double-Precision Floating-Point Value to Doubleword Integer
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_divpd: // Divide Packed Double-Precision Floating-Point Values
case NN_divsd: // Divide Scalar Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_lfence: // Load Fence
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_maskmovdqu: // Store Selected Bytes of Double Quadword
return false;
break;
case NN_maxpd: // Return Maximum Packed Double-Precision Floating-Point Values
case NN_maxsd: // Return Maximum Scalar Double-Precision Floating-Point Value
return this->BuildBinaryRTL(SMP_MAX_S);
break;
case NN_mfence: // Memory Fence
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_minpd: // Return Minimum Packed Double-Precision Floating-Point Values
case NN_minsd: // Return Minimum Scalar Double-Precision Floating-Point Value
return this->BuildBinaryRTL(SMP_MIN_S);
break;
case NN_movapd: // Move Aligned Packed Double-Precision Floating-Point Values
case NN_movdq2q: // Move Quadword from XMM to MMX Register
case NN_movdqa: // Move Aligned Double Quadword
case NN_movdqu: // Move Unaligned Double Quadword
case NN_movhpd: // Move High Packed Double-Precision Floating-Point Values
case NN_movlpd: // Move Low Packed Double-Precision Floating-Point Values
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_movmskpd: // Extract Packed Double-Precision Floating-Point Sign Mask
return false;
break;
case NN_movntdq: // Store Double Quadword Using Non-Temporal Hint
case NN_movnti: // Store Doubleword Using Non-Temporal Hint
case NN_movntpd: // Store Packed Double-Precision Floating-Point Values Using Non-Temporal Hint
case NN_movq2dq: // Move Quadword from MMX to XMM Register
case NN_movsd: // Move Scalar Double-Precision Floating-Point Values
case NN_movupd: // Move Unaligned Packed Double-Precision Floating-Point Values
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
case NN_mulpd: // Multiply Packed Double-Precision Floating-Point Values
case NN_mulsd: // Multiply Scalar Double-Precision Floating-Point Values
case NN_orpd: // Bitwise Logical OR of Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_paddq: // Add Packed Quadword Integers
return this->BuildBinaryRTL(SMP_ADD);
break;
case NN_pause: // Spin Loop Hint
NopRT = new SMPRegTransfer;
NopRT->SetParentInst(this);
NopRT->SetOperator(SMP_NULL_OPERATOR);
this->RTL.push_back(NopRT);
NopRT = NULL;
return true;
case NN_pmuludq: // Multiply Packed Unsigned Doubleword Integers
return this->BuildBinaryRTL(SMP_U_MULTIPLY);
break;
case NN_pshufd: // Shuffle Packed Doublewords
case NN_pshufhw: // Shuffle Packed High Words
case NN_pshuflw: // Shuffle Packed Low Words
return this->BuildBinaryRTL(SMP_SHUFFLE);
break;
case NN_pslldq: // Shift Double Quadword Left Logical
return this->BuildBinaryRTL(SMP_U_LEFT_SHIFT);
break;
case NN_psrldq: // Shift Double Quadword Right Logical
return this->BuildBinaryRTL(SMP_U_RIGHT_SHIFT);
break;
case NN_psubq: // Subtract Packed Quadword Integers
return this->BuildBinaryRTL(SMP_SUBTRACT);
break;
case NN_punpckhqdq: // Unpack High Data
case NN_punpcklqdq: // Unpack Low Data
return this->BuildBinaryRTL(SMP_INTERLEAVE);
break;
case NN_shufpd: // Shuffle Packed Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_SHUFFLE);
break;
case NN_sqrtpd: // Compute Square Roots of Packed Double-Precision Floating-Point Values
case NN_sqrtsd: // Compute Square Rootof Scalar Double-Precision Floating-Point Value
return this->BuildUnary2OpndRTL(SMP_UNARY_FLOATING_ARITHMETIC);
break;
case NN_subpd: // Subtract Packed Double-Precision Floating-Point Values
case NN_subsd: // Subtract Scalar Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_ucomisd: // Unordered Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS
return false;
break;
case NN_unpckhpd: // Unpack and Interleave High Packed Double-Precision Floating-Point Values
case NN_unpcklpd: // Unpack and Interleave Low Packed Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_INTERLEAVE);
break;
case NN_xorpd: // Bitwise Logical OR of Double-Precision Floating-Point Values
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
// AMD syscall/sysret instructions
case NN_syscall: // Low latency system call
case NN_sysret: // Return from system call
// AMD64 instructions
case NN_swapgs: // Exchange GS base with KernelGSBase MSR
// New Pentium instructions (SSE3)
case NN_movddup: // Move One Double-FP and Duplicate
case NN_movshdup: // Move Packed Single-FP High and Duplicate
case NN_movsldup: // Move Packed Single-FP Low and Duplicate
return false;
break;
// Missing AMD64 instructions
case NN_movsxd: // Move with Sign-Extend Doubleword
case NN_cmpxchg16b: // Compare and Exchange 16 Bytes
return false;
break;
// SSE3 instructions
case NN_addsubpd: // Add /Sub packed DP FP numbers
case NN_addsubps: // Add /Sub packed SP FP numbers
case NN_haddpd: // Add horizontally packed DP FP numbers
case NN_haddps: // Add horizontally packed SP FP numbers
case NN_hsubpd: // Sub horizontally packed DP FP numbers
case NN_hsubps: // Sub horizontally packed SP FP numbers
case NN_monitor: // Set up a linear address range to be monitored by hardware
case NN_mwait: // Wait until write-back store performed within the range specified by the MONITOR instruction
return false;
break;
case NN_fisttp: // Store ST in intXX (chop) and pop
return this->BuildBinaryRTL(SMP_CONVERT_FP_TO_INT);
break;
case NN_lddqu: // Load unaligned integer 128-bit
return this->BuildMoveRTL(SMP_NULL_OPERATOR);
break;
// SSSE3 instructions
case NN_psignb: // Packed SIGN Byte
case NN_psignw: // Packed SIGN Word
case NN_psignd: // Packed SIGN Doubleword
case NN_pshufb: // Packed Shuffle Bytes
return this->BuildBinaryRTL(SMP_SHUFFLE);
break;
case NN_pmulhrsw: // Packed Multiply High with Round and Scale
return this->BuildBinaryRTL(SMP_BINARY_FLOATING_ARITHMETIC);
break;
case NN_pmaddubsw: // Multiply and Add Packed Signed and Unsigned Bytes
return this->BuildBinaryRTL(SMP_MULTIPLY_AND_ADD);
break;
case NN_phsubsw: // Packed Horizontal Subtract and Saturate
return this->BuildBinaryRTL(SMP_SUBTRACT);
break;
case NN_phaddsw: // Packed Horizontal Add and Saturate
return this->BuildBinaryRTL(SMP_ADD);
break;
case NN_phaddw: // Packed Horizontal Add Word
case NN_phaddd: // Packed Horizontal Add Doubleword
return this->BuildBinaryRTL(SMP_ADD);
break;
case NN_phsubw: // Packed Horizontal Subtract Word
case NN_phsubd: // Packed Horizontal Subtract Doubleword
return this->BuildBinaryRTL(SMP_SUBTRACT);
break;
case NN_palignr: // Packed Align Right
return this->BuildPackShiftRTL(SMP_CONCATENATE, SMP_REVERSE_SHIFT_U);
break;
case NN_pabsb: // Packed Absolute Value Byte
case NN_pabsw: // Packed Absolute Value Word
case NN_pabsd: // Packed Absolute Value Doubleword
return this->BuildUnary2OpndRTL(SMP_ABSOLUTE_VALUE);
break;
// VMX instructions
case NN_vmcall: // Call to VM Monitor
case NN_vmclear: // Clear Virtual Machine Control Structure
case NN_vmlaunch: // Launch Virtual Machine
case NN_vmresume: // Resume Virtual Machine
case NN_vmptrld: // Load Pointer to Virtual Machine Control Structure
case NN_vmptrst: // Store Pointer to Virtual Machine Control Structure
case NN_vmread: // Read Field from Virtual Machine Control Structure
case NN_vmwrite: // Write Field from Virtual Machine Control Structure
case NN_vmxoff: // Leave VMX Operation
case NN_vmxon: // Enter VMX Operation
return false;
break;
#if 599 < IDA_SDK_VERSION
case NN_ud2: // Undefined Instruction
return false;
break;
// Added with x86-64
case NN_rdtscp: // Read Time-Stamp Counter and Processor ID
return false;
break;
// Geode LX 3DNow! extensions
case NN_pfrcpv: // Reciprocal Approximation for a Pair of 32-bit Floats
case NN_pfrsqrtv: // Reciprocal Square Root Approximation for a Pair of 32-bit Floats
return false;
break;
// SSE2 pseudoinstructions
case NN_cmpeqpd: // Packed Double-FP Compare EQ
case NN_cmpltpd: // Packed Double-FP Compare LT
case NN_cmplepd: // Packed Double-FP Compare LE
case NN_cmpunordpd: // Packed Double-FP Compare UNORD
case NN_cmpneqpd: // Packed Double-FP Compare NOT EQ
case NN_cmpnltpd: // Packed Double-FP Compare NOT LT
case NN_cmpnlepd: // Packed Double-FP Compare NOT LE
case NN_cmpordpd: // Packed Double-FP Compare ORDERED
case NN_cmpeqsd: // Scalar Double-FP Compare EQ
case NN_cmpltsd: // Scalar Double-FP Compare LT
case NN_cmplesd: // Scalar Double-FP Compare LE
case NN_cmpunordsd: // Scalar Double-FP Compare UNORD
case NN_cmpneqsd: // Scalar Double-FP Compare NOT EQ
case NN_cmpnltsd: // Scalar Double-FP Compare NOT LT
case NN_cmpnlesd: // Scalar Double-FP Compare NOT LE
case NN_cmpordsd: // Scalar Double-FP Compare ORDERED
return false;
break;
// SSSE4.1 instructions
case NN_blendpd: // Blend Packed Double Precision Floating-Point Values
case NN_blendps: // Blend Packed Single Precision Floating-Point Values
case NN_blendvpd: // Variable Blend Packed Double Precision Floating-Point Values
case NN_blendvps: // Variable Blend Packed Single Precision Floating-Point Values
case NN_dppd: // Dot Product of Packed Double Precision Floating-Point Values
case NN_dpps: // Dot Product of Packed Single Precision Floating-Point Values
return false;
break;
case NN_extractps: // Extract Packed Single Precision Floating-Point Value
case NN_insertps: // Insert Packed Single Precision Floating-Point Value
case NN_movntdqa: // Load Double Quadword Non-Temporal Aligned Hint
case NN_mpsadbw: // Compute Multiple Packed Sums of Absolute Difference
case NN_packusdw: // Pack with Unsigned Saturation
case NN_pblendvb: // Variable Blend Packed Bytes
case NN_pblendw: // Blend Packed Words
case NN_pcmpeqq: // Compare Packed Qword Data for Equal
return false;
break;
case NN_pextrb: // Extract Byte
case NN_pextrd: // Extract Dword
case NN_pextrq: // Extract Qword
return this->BuildBinaryPlusImmedRTL(SMP_EXTRACT_ZERO_EXTEND, SMP_CREATE_MASK);
case NN_phminposuw: // Packed Horizontal Word Minimum
return false;
break;
case NN_pinsrb: // Insert Byte
case NN_pinsrd: // Insert Dword
case NN_pinsrq: // Insert Qword
return this->BuildBinaryPlusImmedRTL(SMP_BITWISE_AND, SMP_CREATE_MASK);
break;
case NN_pmaxsb: // Maximum of Packed Signed Byte Integers
case NN_pmaxsd: // Maximum of Packed Signed Dword Integers
case NN_pmaxud: // Maximum of Packed Unsigned Dword Integers
case NN_pmaxuw: // Maximum of Packed Word Integers
case NN_pminsb: // Minimum of Packed Signed Byte Integers
case NN_pminsd: // Minimum of Packed Signed Dword Integers
case NN_pminud: // Minimum of Packed Unsigned Dword Integers
case NN_pminuw: // Minimum of Packed Word Integers
case NN_pmovsxbw: // Packed Move with Sign Extend
case NN_pmovsxbd: // Packed Move with Sign Extend
case NN_pmovsxbq: // Packed Move with Sign Extend
case NN_pmovsxwd: // Packed Move with Sign Extend
case NN_pmovsxwq: // Packed Move with Sign Extend
case NN_pmovsxdq: // Packed Move with Sign Extend
case NN_pmovzxbw: // Packed Move with Zero Extend
case NN_pmovzxbd: // Packed Move with Zero Extend
case NN_pmovzxbq: // Packed Move with Zero Extend
case NN_pmovzxwd: // Packed Move with Zero Extend
case NN_pmovzxwq: // Packed Move with Zero Extend
case NN_pmovzxdq: // Packed Move with Zero Extend
case NN_pmuldq: // Multiply Packed Signed Dword Integers
case NN_pmulld: // Multiply Packed Signed Dword Integers and Store Low Result
return false;
break;
case NN_ptest: // Logical Compare
return this->BuildFlagsDestBinaryRTL(SMP_U_COMPARE);
case NN_roundpd: // Round Packed Double Precision Floating-Point Values
case NN_roundps: // Round Packed Single Precision Floating-Point Values
case NN_roundsd: // Round Scalar Double Precision Floating-Point Values
case NN_roundss: // Round Scalar Single Precision Floating-Point Values
return false;
break;
// SSSE4.2 instructions
case NN_crc32: // Accumulate CRC32 Value
return false;
break;
case NN_pcmpestri: // Packed Compare Explicit Length Strings, Return Index
case NN_pcmpestrm: // Packed Compare Explicit Length Strings, Return Mask
case NN_pcmpistri: // Packed Compare Implicit Length Strings, Return Index
case NN_pcmpistrm: // Packed Compare Implicit Length Strings, Return Mask
return BuildBinaryIgnoreImmedRTL(SMP_GENERAL_COMPARE);
break;
case NN_pcmpgtq: // Compare Packed Data for Greater Than
case NN_popcnt: // Return the Count of Number of Bits Set to 1
return false;
break;
// AMD SSE4a instructions
case NN_extrq: // Extract Field From Register
case NN_insertq: // Insert Field
case NN_movntsd: // Move Non-Temporal Scalar Double-Precision Floating-Point
case NN_movntss: // Move Non-Temporal Scalar Single-Precision Floating-Point
case NN_lzcnt: // Leading Zero Count
return false;
break;
// xsave/xrstor instructions
case NN_xgetbv: // Get Value of Extended Control Register
return this->BuildOptType8RTL();
case NN_xrstor: // Restore Processor Extended States
case NN_xsave: // Save Processor Extended States
case NN_xsetbv: // Set Value of Extended Control Register
return false;
break;
// Intel Safer Mode Extensions (SMX)
case NN_getsec: // Safer Mode Extensions (SMX) Instruction
return false;
break;
// AMD-V Virtualization ISA Extension
case NN_clgi: // Clear Global Interrupt Flag
case NN_invlpga: // Invalidate TLB Entry in a Specified ASID
case NN_skinit: // Secure Init and Jump with Attestation
case NN_stgi: // Set Global Interrupt Flag
case NN_vmexit: // Stop Executing Guest, Begin Executing Host
case NN_vmload: // Load State from VMCB
case NN_vmmcall: // Call VMM
case NN_vmrun: // Run Virtual Machine
case NN_vmsave: // Save State to VMCB
return false;
break;
// VMX+ instructions
case NN_invept: // Invalidate Translations Derived from EPT
case NN_invvpid: // Invalidate Translations Based on VPID
return false;
break;
// Intel Atom instructions
case NN_movbe: // Move Data After Swapping Bytes
return false;
break;
// Intel AES instructions
case NN_aesenc: // Perform One Round of an AES Encryption Flow
case NN_aesenclast: // Perform the Last Round of an AES Encryption Flow
case NN_aesdec: // Perform One Round of an AES Decryption Flow
case NN_aesdeclast: // Perform the Last Round of an AES Decryption Flow
case NN_aesimc: // Perform the AES InvMixColumn Transformation
case NN_aeskeygenassist: // AES Round Key Generation Assist
return this->BuildBinaryRTL(SMP_ENCRYPTION_OPERATION);
break;
// Carryless multiplication
case NN_pclmulqdq: // Carry-Less Multiplication Quadword
return BuildBinaryIgnoreImmedRTL(SMP_U_MULTIPLY);
break;
#endif // 599 < IDA_SDK_VERSION
default:
SMP_msg("ERROR: Unknown instruction opcode at %x : %s\n", this->GetAddr(),
DisAsmText.GetDisAsm(this->GetAddr()));
return false;
break;
} // end switch on opcode
return true;
} // end SMPInstr::BuildRTL()
// Iterate through all reg transfers and call SyncRTLDefUse for each.
void SMPInstr::SyncAllRTs(bool UseFP, sval_t FPDelta) {
for (size_t index = 0; index < this->RTL.GetCount(); ++index) {
this->SyncRTLDefUse(this->RTL.GetRT(index), UseFP, FPDelta);
}
return;
} // end of SMPInstr:SyncAllRTs()
// Ensure that each operand of the RTL is found in the appropriate DEF or USE list.
void SMPInstr::SyncRTLDefUse(SMPRegTransfer *CurrRT, bool UseFP, sval_t FPDelta) {
// The ExtraKills are almost never represented in the DEF
// lists. When they are, they are added in MDFixupDefUseLists(), so we ignore them here.
// The only DEFs should come from left operands of SMP_ASSIGN operators, i.e. the effects
// of register transfers.
op_t LeftOp, RightOp;
set<DefOrUse, LessDefUse>::iterator CurrDef, CurrUse;
bool DebugFlag = false;
#if SMP_VERBOSE_DEBUG_BUILD_RTL
DebugFlag |= (0 == strcmp("__libc_csu_fini", this->BasicBlock->GetFunc()->GetFuncName()));
#endif
if (DebugFlag) {
SMP_msg("SyncRTLDefUse entered. Dump of USE list:\n");
this->Uses.Dump();
}
LeftOp = CurrRT->GetLeftOperand();
if (SMP_ASSIGN == CurrRT->GetOperator()) {
assert(o_void != LeftOp.type);
assert(o_imm != LeftOp.type);
CurrDef = this->Defs.FindRef(LeftOp);
if (CurrDef == this->GetLastDef() && !LeftOp.is_reg(R_ip)) {
#if SMP_VERBOSE_DEBUG_BUILD_RTL
SMP_msg("WARNING: DEF not found for SMP_ASSIGN in %s ; added op:", DisAsmText.GetDisAsm(this->GetAddr()));
PrintOperand(LeftOp);
SMP_msg("\n");
#endif
this->Defs.SetRef(LeftOp, CurrRT->GetOperatorType());
}
}
else { // not SMP_ASSIGN; left operand should be a USE
if (o_void != LeftOp.type) {
CurrUse = this->Uses.FindRef(LeftOp);
if (CurrUse == this->GetLastUse()) {
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: USE not found for ");
PrintOperand(LeftOp);
SMP_msg(" in %s ; added\n", DisAsmText.GetDisAsm(this->GetAddr()));
#endif
this->Uses.SetRef(LeftOp);
}
}
}
if (!CurrRT->HasRightSubTree()) {
RightOp = CurrRT->GetRightOperand(); // right operand should be a USE
if (o_void != RightOp.type) {
CurrUse = this->Uses.FindRef(RightOp);
if (CurrUse == this->GetLastUse()) {
#if SMP_VERBOSE_DEBUG_BUILD_RTL_DEF_USE
SMP_msg("WARNING: USE not found for ");
PrintOperand(RightOp);
SMP_msg(" in %s ; added\n", DisAsmText.GetDisAsm(this->GetAddr()));
#endif
this->Uses.SetRef(RightOp);
}
}
}
else { // recurse into right subtree
this->SyncRTLDefUse(CurrRT->GetRightTree(), UseFP, FPDelta);
}
// Guard operands can only be USEs.
SMPGuard *GuardExpr = CurrRT->GetGuard();
if (NULL != GuardExpr) {
LeftOp = GuardExpr->GetLeftOperand();
if ((o_void != LeftOp.type) && (o_imm != LeftOp.type)) {
CurrUse = this->Uses.FindRef(LeftOp);
if (CurrUse == this->GetLastUse()) {
this->Uses.SetRef(LeftOp);
}
}
RightOp = GuardExpr->GetRightOperand();
if ((o_void != RightOp.type) && (o_imm != RightOp.type)) {
CurrUse = this->Uses.FindRef(RightOp);
if (CurrUse == this->GetLastUse()) {
this->Uses.SetRef(RightOp);
}
}
}
return;
} // end of SMPInstr::SyncRTLDefUse()
// SetOperatorType - set the type of the operator, take into account the speculative (profiler) status
void SMPRegTransfer::SetOperatorType(SMPOperandType OpType, const SMPInstr* Instr) {
SMPOperandType OldType = RTop.type;
SMPOperandType NewType = OpType;
if (Instr->GetBlock()->GetFunc()->GetIsSpeculative()) {
NewType = (SMPOperandType) (((int)NewType) | PROF_BASE);
#if SMP_TRACK_NONSPEC_OPER_TYPE
if (!IsProfDerived(OldType))
RTop.NonSpeculativeType = OldType;
#endif
}
RTop.type = NewType;
} // end of SMPRegTransfer::SetOperatorType
// Does UseOp arithmetically affect the value of the NonFlagsDef for this RTL?
bool SMPRegTransfer::OperandTransfersValueToDef(op_t UseOp) const {
bool FoundTransfer = false;
op_t DefOp = this->GetLeftOperand();
SMPoperator CurrOp = this->GetOperator();
if ((SMP_ASSIGN == CurrOp) && (DefOp.type != o_void) && (!DefOp.is_reg(MD_FLAGS_REG))) {
// We have an assignment to a non-flag DefOp. The only remaining question is whether
// UseOp appears in the right hand side of the RT as something besides an address register
// inside a memory operand.
if (this->HasRightSubTree()) {
FoundTransfer = this->GetRightTree()->OperandTransfersHelper(UseOp);
}
else {
op_t RightOp = this->GetRightOperand();
if (IsEqOpIgnoreBitwidth(UseOp, RightOp)) {
// Found UseOp.
FoundTransfer = true;
}
}
}
return FoundTransfer;
} // end of SMPRegTransfer::OperandTransfersValueToDef()
// Does UseOp arithmetically affect the value of the NonFlagsDef for this RT?
// Recursive helper for OperandTransfersValueToDef().
bool SMPRegTransfer::OperandTransfersHelper(op_t UseOp) const {
bool FoundTransfer = false;
op_t LeftOp = this->GetLeftOperand();
SMPoperator CurrOp = this->GetOperator();
// Have to check left and right operands to see if they are UseOp.
if (IsEqOpIgnoreBitwidth(UseOp, LeftOp)) {
FoundTransfer = true; // Found UseOp.
}
else if (this->HasRightSubTree()) { // recurse
FoundTransfer = this->GetRightTree()->OperandTransfersHelper(UseOp);
}
else {
op_t RightOp = this->GetRightOperand();
if (IsEqOpIgnoreBitwidth(UseOp, RightOp)) {
// Found UseOp.
FoundTransfer = true;
}
}
return FoundTransfer;
} // end of SMPRegTransfer::OperandTransfersHelper()
// Update the memory source operands to have the new type from profiling info.
void SMPInstr::UpdateMemLoadTypes(SMPOperandType newType) {
bool MemSrc = false;
op_t Opnd;
set<DefOrUse, LessDefUse>::iterator UseIter;
for (UseIter = this->GetFirstUse(); UseIter != this->GetLastUse(); ++UseIter) {
Opnd = UseIter->GetOp();
optype_t CurrType = Opnd.type;
MemSrc = ((CurrType == o_mem) || (CurrType == o_phrase) || (CurrType == o_displ));
if (MemSrc) {
SMPOperandType type = UseIter->GetType();
assert(newType == (NUMERIC|PROF_BASE));
if (type == UNINIT) {
this->SetUseType(Opnd, newType);
break;
}
else if (type >= POINTER) {
this->SetUseType(Opnd, (SMPOperandType)(UNKNOWN|PROF_BASE));
break;
}
}
}
return ;
} // end of SMPInstr::UpdateMemLoadTypes()
// Return true if we have register DefOp += ImmOp.
bool SMPInstr::MDIsAddImmediateToReg(op_t &DefOp, op_t &ImmOp) {
bool FoundAddImmed = false;
bool FoundImmed = false;
bool FoundRegUse = false;
if (NN_add == this->SMPcmd.itype) {
set<DefOrUse, LessDefUse>::iterator UseIter = this->GetFirstUse();
while (UseIter != this->GetLastUse()) {
op_t UseOp = UseIter->GetOp();
if (o_imm == UseOp.type) {
ImmOp = UseOp;
FoundImmed = true;
}
else if (o_reg == UseOp.type) {
set<DefOrUse, LessDefUse>::iterator DefIter = this->GetFirstNonFlagsDef();
op_t TempDefOp = DefIter->GetOp();
if (o_reg != TempDefOp.type) {
return false;
}
if (MDLessReg(UseOp.reg, TempDefOp.reg) || MDLessReg(TempDefOp.reg, UseOp.reg)) {
return false;
}
// If we make it here, we have the same register DEFed as we found USEd.
DefOp = TempDefOp;
FoundRegUse = true;
}
++UseIter;
}
FoundAddImmed = (FoundImmed && FoundRegUse);
}
return FoundAddImmed;
} // end of SMPInstr::MDIsAddImmediateToReg()