mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-10-12 21:27:01 +02:00
git-subtree-dir: dmd2 git-subtree-split: 10017d50eaaff4ecdc37a0153b6c37ea0b004c81
3207 lines
94 KiB
C
3207 lines
94 KiB
C
// Copyright (C) 1995-1998 by Symantec
|
|
// Copyright (C) 2000-2009 by Digital Mars
|
|
// All Rights Reserved
|
|
// http://www.digitalmars.com
|
|
// Written by Walter Bright
|
|
/*
|
|
* This source file is made available for personal use
|
|
* only. The license is in /dmd/src/dmd/backendlicense.txt
|
|
* or /dm/src/dmd/backendlicense.txt
|
|
* For any other uses, please contact Digital Mars.
|
|
*/
|
|
|
|
#if !SPP
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "cc.h"
|
|
#include "el.h"
|
|
#include "code.h"
|
|
#include "oper.h"
|
|
#include "global.h"
|
|
#include "type.h"
|
|
#include "exh.h"
|
|
#include "list.h"
|
|
|
|
static char __file__[] = __FILE__; /* for tassert.h */
|
|
#include "tassert.h"
|
|
|
|
// If we use Pentium Pro scheduler
|
|
#if 0
|
|
#define PRO (config.target_scheduler >= TARGET_PentiumPro)
|
|
#else
|
|
#define PRO (config.target_cpu >= TARGET_PentiumPro)
|
|
#endif
|
|
|
|
// Struct where we gather information about an instruction
|
|
struct Cinfo
|
|
{
|
|
code *c; // the instruction
|
|
unsigned char pair; // pairing information
|
|
unsigned char sz; // operand size
|
|
unsigned char isz; // instruction size
|
|
|
|
// For floating point scheduling
|
|
unsigned char fxch_pre;
|
|
unsigned char fxch_post;
|
|
unsigned char fp_op;
|
|
#define FPfstp 1 // FSTP mem
|
|
#define FPfld 2 // FLD mem
|
|
#define FPfop 3 // Fop ST0,mem or Fop ST0
|
|
|
|
unsigned char flags;
|
|
#define CIFLarraybounds 1 // this instruction is a jmp to array bounds
|
|
#define CIFLea 2 // this instruction has a memory-referencing
|
|
// modregrm EA byte
|
|
#define CIFLnostage 4 // don't stage these instructions
|
|
#define CIFLpush 8 // it's a push we can swap around
|
|
|
|
unsigned r; // read mask
|
|
unsigned w; // write mask
|
|
unsigned a; // registers used in addressing mode
|
|
unsigned char reg; // reg field of modregrm byte
|
|
unsigned char uops; // Pentium Pro micro-ops
|
|
unsigned sibmodrm; // (sib << 8) + mod__rm byte
|
|
unsigned spadjust; // if !=0, then amount ESP changes as a result of this
|
|
// instruction being executed
|
|
int fpuadjust; // if !=0, then amount FPU stack changes as a result
|
|
// of this instruction being executed
|
|
#if DEBUG
|
|
void print(); // pretty-printer
|
|
#endif
|
|
};
|
|
|
|
code *simpleops(code *c,regm_t scratch);
|
|
code *schedule(code *c,regm_t scratch);
|
|
code *peephole(code *c,regm_t scratch);
|
|
|
|
/*****************************************
|
|
* Do Pentium optimizations.
|
|
* Input:
|
|
* scratch scratch registers we can use
|
|
*/
|
|
|
|
void cgsched_pentium(code **pc,regm_t scratch)
|
|
{
|
|
//printf("scratch = x%02x\n",scratch);
|
|
if (config.target_scheduler >= TARGET_80486)
|
|
{
|
|
if (!I64)
|
|
*pc = peephole(*pc,0);
|
|
if (I32) // forget about 16 bit code
|
|
{
|
|
if (config.target_cpu == TARGET_Pentium ||
|
|
config.target_cpu == TARGET_PentiumMMX)
|
|
*pc = simpleops(*pc,scratch);
|
|
*pc = schedule(*pc,0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cgsched_block(block* b)
|
|
{
|
|
if (config.flags4 & CFG4speed &&
|
|
config.target_cpu >= TARGET_Pentium &&
|
|
b->BC != BCasm)
|
|
{
|
|
regm_t scratch = allregs;
|
|
|
|
scratch &= ~(b->Bregcon.used | b->Bregcon.params | mfuncreg);
|
|
scratch &= ~(b->Bregcon.immed.mval | b->Bregcon.cse.mval);
|
|
cgsched_pentium(&b->Bcode,scratch);
|
|
//printf("after schedule:\n"); WRcodlst(b->Bcode);
|
|
}
|
|
}
|
|
|
|
#define NP 0 // not pairable
|
|
#define PU 1 // pairable in U only, never executed in V
|
|
#define PV 2 // pairable in V only
|
|
#define UV (PU|PV) // pairable in both U and V
|
|
#define PE 4 // register contention exception
|
|
#define PF 8 // flags contention exception
|
|
#define FX 0x10 // pairable with FXCH instruction
|
|
|
|
static unsigned char pentcycl[256] =
|
|
{
|
|
UV,UV,UV,UV, UV,UV,NP,NP, // 0
|
|
UV,UV,UV,UV, UV,UV,NP,NP, // 8
|
|
PU,PU,PU,PU, PU,PU,NP,NP, // 10
|
|
PU,PU,PU,PU, PU,PU,NP,NP, // 18
|
|
UV,UV,UV,UV, UV,UV,NP,NP, // 20
|
|
UV,UV,UV,UV, UV,UV,NP,NP, // 28
|
|
UV,UV,UV,UV, UV,UV,NP,NP, // 30
|
|
UV,UV,UV,UV, UV,UV,NP,NP, // 38
|
|
|
|
UV,UV,UV,UV, UV,UV,UV,UV, // 40
|
|
UV,UV,UV,UV, UV,UV,UV,UV, // 48
|
|
PE|UV,PE|UV,PE|UV,PE|UV, PE|UV,PE|UV,PE|UV,PE|UV, // 50 PUSH reg
|
|
PE|UV,PE|UV,PE|UV,PE|UV, PE|UV,PE|UV,PE|UV,PE|UV, // 58 POP reg
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // 60
|
|
PE|UV,NP,PE|UV,NP, NP,NP,NP,NP, // 68
|
|
PV|PF,PV|PF,PV|PF,PV|PF, PV|PF,PV|PF,PV|PF,PV|PF, // 70 Jcc rel8
|
|
PV|PF,PV|PF,PV|PF,PV|PF, PV|PF,PV|PF,PV|PF,PV|PF, // 78 Jcc rel8
|
|
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // 80
|
|
UV,UV,UV,UV, NP,UV,NP,NP, // 88
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // 90
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // 98
|
|
UV,UV,UV,UV, NP,NP,NP,NP, // A0
|
|
UV,UV,NP,NP, NP,NP,NP,NP, // A8
|
|
UV,UV,UV,UV, UV,UV,UV,UV, // B0
|
|
UV,UV,UV,UV, UV,UV,UV,UV, // B8
|
|
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // C0
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // C8
|
|
PU,PU,NP,NP, NP,NP,NP,NP, // D0
|
|
FX,NP,FX,FX, NP,NP,FX,NP, // D8 all floating point
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // E0
|
|
PE|PV,PV,NP,PV, NP,NP,NP,NP, // E8
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // F0
|
|
NP,NP,NP,NP, NP,NP,NP,NP, // F8
|
|
};
|
|
|
|
/********************************************
|
|
* For each opcode, determine read [0] and written [1] masks.
|
|
*/
|
|
|
|
#define EA 0x100000
|
|
#define R 0x200000 // register (reg of modregrm field)
|
|
#define N 0x400000 // other things modified, not swappable
|
|
#define B 0x800000 // it's a byte operation
|
|
#define C 0x1000000 // floating point flags
|
|
#define mMEM 0x2000000 // memory
|
|
#define S 0x4000000 // floating point stack
|
|
#define F 0x8000000 // flags
|
|
|
|
static unsigned oprw[256][2] =
|
|
{
|
|
// 00
|
|
EA|R|B, F|EA|B, // ADD
|
|
EA|R, F|EA,
|
|
EA|R|B, F|R|B,
|
|
EA|R, F|R,
|
|
mAX, F|mAX,
|
|
mAX, F|mAX,
|
|
N, N, // PUSH ES
|
|
N, N, // POP ES
|
|
|
|
// 08
|
|
EA|R|B, F|EA|B, // OR
|
|
EA|R, F|EA,
|
|
EA|R|B, F|R|B,
|
|
EA|R, F|R,
|
|
mAX, F|mAX,
|
|
mAX, F|mAX,
|
|
N, N, // PUSH CS
|
|
N, N, // 2 byte escape
|
|
|
|
// 10
|
|
F|EA|R|B,F|EA|B, // ADC
|
|
F|EA|R, F|EA,
|
|
F|EA|R|B,F|R|B,
|
|
F|EA|R, F|R,
|
|
F|mAX, F|mAX,
|
|
F|mAX, F|mAX,
|
|
N, N, // PUSH SS
|
|
N, N, // POP SS
|
|
|
|
// 18
|
|
F|EA|R|B,F|EA|B, // SBB
|
|
F|EA|R, F|EA,
|
|
F|EA|R|B,F|R|B,
|
|
F|EA|R, F|R,
|
|
F|mAX, F|mAX,
|
|
F|mAX, F|mAX,
|
|
N, N, // PUSH DS
|
|
N, N, // POP DS
|
|
|
|
// 20
|
|
EA|R|B, F|EA|B, // AND
|
|
EA|R, F|EA,
|
|
EA|R|B, F|R|B,
|
|
EA|R, F|R,
|
|
mAX, F|mAX,
|
|
mAX, F|mAX,
|
|
N, N, // SEG ES
|
|
F|mAX, F|mAX, // DAA
|
|
|
|
// 28
|
|
EA|R|B, F|EA|B, // SUB
|
|
EA|R, F|EA,
|
|
EA|R|B, F|R|B,
|
|
EA|R, F|R,
|
|
mAX, F|mAX,
|
|
mAX, F|mAX,
|
|
N, N, // SEG CS
|
|
F|mAX, F|mAX, // DAS
|
|
|
|
// 30
|
|
EA|R|B, F|EA|B, // XOR
|
|
EA|R, F|EA,
|
|
EA|R|B, F|R|B,
|
|
EA|R, F|R,
|
|
mAX, F|mAX,
|
|
mAX, F|mAX,
|
|
N, N, // SEG SS
|
|
F|mAX, F|mAX, // AAA
|
|
|
|
// 38
|
|
EA|R|B, F, // CMP
|
|
EA|R, F,
|
|
EA|R|B, F,
|
|
EA|R, F,
|
|
mAX, F, // CMP AL,imm8
|
|
mAX, F, // CMP EAX,imm16/32
|
|
N, N, // SEG DS
|
|
N, N, // AAS
|
|
|
|
// 40
|
|
mAX, F|mAX, // INC EAX
|
|
mCX, F|mCX,
|
|
mDX, F|mDX,
|
|
mBX, F|mBX,
|
|
mSP, F|mSP,
|
|
mBP, F|mBP,
|
|
mSI, F|mSI,
|
|
mDI, F|mDI,
|
|
|
|
// 48
|
|
mAX, F|mAX, // DEC EAX
|
|
mCX, F|mCX,
|
|
mDX, F|mDX,
|
|
mBX, F|mBX,
|
|
mSP, F|mSP,
|
|
mBP, F|mBP,
|
|
mSI, F|mSI,
|
|
mDI, F|mDI,
|
|
|
|
// 50
|
|
mAX|mSP, mSP|mMEM, // PUSH EAX
|
|
mCX|mSP, mSP|mMEM,
|
|
mDX|mSP, mSP|mMEM,
|
|
mBX|mSP, mSP|mMEM,
|
|
mSP|mSP, mSP|mMEM,
|
|
mBP|mSP, mSP|mMEM,
|
|
mSI|mSP, mSP|mMEM,
|
|
mDI|mSP, mSP|mMEM,
|
|
|
|
// 58
|
|
mSP|mMEM, mAX|mSP, // POP EAX
|
|
mSP|mMEM, mCX|mSP,
|
|
mSP|mMEM, mDX|mSP,
|
|
mSP|mMEM, mBX|mSP,
|
|
mSP|mMEM, mSP|mSP,
|
|
mSP|mMEM, mBP|mSP,
|
|
mSP|mMEM, mSI|mSP,
|
|
mSP|mMEM, mDI|mSP,
|
|
|
|
// 60
|
|
N, N, // PUSHA
|
|
N, N, // POPA
|
|
N, N, // BOUND Gv,Ma
|
|
N, N, // ARPL Ew,Rw
|
|
N, N, // SEG FS
|
|
N, N, // SEG GS
|
|
N, N, // operand size prefix
|
|
N, N, // address size prefix
|
|
|
|
// 68
|
|
mSP, mSP|mMEM, // PUSH immed16/32
|
|
EA, F|R, // IMUL Gv,Ev,lv
|
|
mSP, mSP|mMEM, // PUSH immed8
|
|
EA, F|R, // IMUL Gv,Ev,lb
|
|
N, N, // INSB Yb,DX
|
|
N, N, // INSW/D Yv,DX
|
|
N, N, // OUTSB DX,Xb
|
|
N, N, // OUTSW/D DX,Xv
|
|
|
|
// 70
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
|
|
// 78
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
F|N, N,
|
|
|
|
// 80
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
EA|R, F, // TEST EA,r8
|
|
EA|R, F, // TEST EA,r16/32
|
|
EA|R, EA|R, // XCHG EA,r8
|
|
EA|R, EA|R, // XCHG EA,r16/32
|
|
|
|
// 88
|
|
R|B, EA|B, // MOV EA8,r8
|
|
R, EA, // MOV EA,r16/32
|
|
EA|B, R|B, // MOV r8,EA8
|
|
EA, R, // MOV r16/32,EA
|
|
N, N, // MOV EA,segreg
|
|
EA, R, // LEA r16/32,EA
|
|
N, N, // MOV segreg,EA
|
|
mSP|mMEM, EA|mSP, // POP mem16/32
|
|
|
|
// 90
|
|
0, 0, // NOP
|
|
mAX|mCX, mAX|mCX,
|
|
mAX|mDX, mAX|mDX,
|
|
mAX|mBX, mAX|mBX,
|
|
mAX|mSP, mAX|mSP,
|
|
mAX|mBP, mAX|mBP,
|
|
mAX|mSI, mAX|mSI,
|
|
mAX|mDI, mAX|mDI,
|
|
|
|
// 98
|
|
mAX, mAX, // CBW
|
|
mAX, mDX, // CWD
|
|
N, N|F, // CALL far ptr
|
|
N, N, // WAIT
|
|
F|mSP, mSP|mMEM, // PUSHF
|
|
mSP|mMEM, F|mSP, // POPF
|
|
mAX, F, // SAHF
|
|
F, mAX, // LAHF
|
|
|
|
// A0
|
|
mMEM, mAX, // MOV AL,moffs8
|
|
mMEM, mAX, // MOV EAX,moffs32
|
|
mAX, mMEM, // MOV moffs8,AL
|
|
mAX, mMEM, // MOV moffs32,EAX
|
|
N, N, // MOVSB
|
|
N, N, // MOVSW/D
|
|
N, N, // CMPSB
|
|
N, N, // CMPSW/D
|
|
|
|
// A8
|
|
mAX, F, // TEST AL,imm8
|
|
mAX, F, // TEST AX,imm16
|
|
N, N, // STOSB
|
|
N, N, // STOSW/D
|
|
N, N, // LODSB
|
|
N, N, // LODSW/D
|
|
N, N, // SCASB
|
|
N, N, // SCASW/D
|
|
|
|
// B0
|
|
0, mAX, // MOV AL,imm8
|
|
0, mCX,
|
|
0, mDX,
|
|
0, mBX,
|
|
0, mAX,
|
|
0, mCX,
|
|
0, mDX,
|
|
0, mBX,
|
|
|
|
// B8
|
|
0, mAX, // MOV AX,imm16
|
|
0, mCX,
|
|
0, mDX,
|
|
0, mBX,
|
|
0, mSP,
|
|
0, mBP,
|
|
0, mSI,
|
|
0, mDI,
|
|
|
|
// C0
|
|
EA, F|EA, // Shift Eb,Ib
|
|
EA, F|EA,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
0, EA|B, // MOV EA8,imm8
|
|
0, EA, // MOV EA,imm16
|
|
|
|
// C8
|
|
N, N, // ENTER
|
|
N, N, // LEAVE
|
|
N, N, // RETF lw
|
|
N, N, // RETF
|
|
N, N, // INT 3
|
|
N, N, // INT lb
|
|
N, N, // INTO
|
|
N, N, // IRET
|
|
|
|
// D0
|
|
EA, F|EA, // Shift EA,1
|
|
EA, F|EA,
|
|
EA|mCX, F|EA, // Shift EA,CL
|
|
EA|mCX, F|EA,
|
|
mAX, F|mAX, // AAM
|
|
mAX, F|mAX, // AAD
|
|
N, N, // reserved
|
|
mAX|mBX|mMEM, mAX, // XLAT
|
|
|
|
// D8
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
N, N,
|
|
|
|
// E0
|
|
F|mCX|N,mCX|N, // LOOPNE jb
|
|
F|mCX|N,mCX|N, // LOOPE jb
|
|
mCX|N, mCX|N, // LOOP jb
|
|
mCX|N, N, // JCXZ jb
|
|
N, N, // IN AL,lb
|
|
N, N, // IN EAX,lb
|
|
N, N, // OUT lb,AL
|
|
N, N, // OUT lb,EAX
|
|
|
|
// E8
|
|
N, N|F, // CALL jv
|
|
N, N, // JMP Jv
|
|
N, N, // JMP Ab
|
|
N, N, // JMP jb
|
|
N|mDX, N|mAX, // IN AL,DX
|
|
N|mDX, N|mAX, // IN AX,DX
|
|
N|mAX|mDX,N, // OUT DX,AL
|
|
N|mAX|mDX,N, // OUT DX,AX
|
|
|
|
// F0
|
|
N, N, // LOCK
|
|
N, N, // reserved
|
|
N, N, // REPNE
|
|
N, N, // REP,REPE
|
|
N, N, // HLT
|
|
F, F, // CMC
|
|
N, N,
|
|
N, N,
|
|
|
|
// F8
|
|
0, F, // CLC
|
|
0, F, // STC
|
|
N, N, // CLI
|
|
N, N, // STI
|
|
N, N, // CLD
|
|
N, N, // STD
|
|
EA, F|EA, // INC/DEC
|
|
N, N,
|
|
};
|
|
|
|
/****************************************
|
|
* Same thing, but for groups.
|
|
*/
|
|
|
|
static unsigned grprw[8][8][2] =
|
|
{
|
|
// Grp 1
|
|
EA, F|EA, // ADD
|
|
EA, F|EA, // OR
|
|
F|EA, F|EA, // ADC
|
|
F|EA, F|EA, // SBB
|
|
EA, F|EA, // AND
|
|
EA, F|EA, // SUB
|
|
EA, F|EA, // XOR
|
|
EA, F, // CMP
|
|
|
|
// Grp 3
|
|
EA, F, // TEST EA,imm
|
|
N, N, // reserved
|
|
EA, EA, // NOT
|
|
EA, F|EA, // NEG
|
|
mAX|EA, F|mAX|mDX, // MUL
|
|
mAX|EA, F|mAX|mDX, // IMUL
|
|
mAX|mDX|EA, F|mAX|mDX, // DIV
|
|
#if 0
|
|
// Could generate an exception we want to catch
|
|
mAX|mDX|EA|N, F|mAX|mDX|N, // IDIV
|
|
#else
|
|
mAX|mDX|EA, F|mAX|mDX, // IDIV
|
|
#endif
|
|
|
|
// Grp 5
|
|
EA, F|EA, // INC Ev
|
|
EA, F|EA, // DEC Ev
|
|
N|EA, N, // CALL Ev
|
|
N|EA, N, // CALL eP
|
|
N|EA, N, // JMP Ev
|
|
N|EA, N, // JMP Ep
|
|
mSP|EA, mSP|mMEM, // PUSH Ev
|
|
N, N, // reserved
|
|
|
|
// Grp 3, byte version
|
|
EA|B, F, // TEST EA,imm
|
|
N, N, // reserved
|
|
EA|B, EA|B, // NOT
|
|
EA|B, F|EA|B, // NEG
|
|
mAX|EA, F|mAX, // MUL
|
|
mAX|EA, F|mAX, // IMUL
|
|
mAX|EA, F|mAX, // DIV
|
|
#if 0
|
|
// Could generate an exception we want to catch
|
|
mAX|EA|N, F|mAX|N, // IDIV
|
|
#else
|
|
mAX|EA, F|mAX, // IDIV
|
|
#endif
|
|
|
|
};
|
|
|
|
/********************************************
|
|
* For floating point opcodes 0xD8..0xDF, with Irm < 0xC0.
|
|
* [][][0] = read
|
|
* [1] = write
|
|
*/
|
|
|
|
static unsigned grpf1[8][8][2] =
|
|
{
|
|
// 0xD8
|
|
EA|S, S|C, // FADD float
|
|
EA|S, S|C, // FMUL float
|
|
EA|S, C, // FCOM float
|
|
EA|S, S|C, // FCOMP float
|
|
EA|S, S|C, // FSUB float
|
|
EA|S, S|C, // FSUBR float
|
|
EA|S, S|C, // FDIV float
|
|
EA|S, S|C, // FDIVR float
|
|
|
|
// 0xD9
|
|
EA, S|C, // FLD float
|
|
N, N, //
|
|
S, EA|C, // FST float
|
|
S, EA|S|C, // FSTP float
|
|
N, N, // FLDENV
|
|
N, N, // FLDCW
|
|
N, N, // FSTENV
|
|
N, N, // FSTCW
|
|
|
|
// 0xDA
|
|
EA|S, S|C, // FIADD long
|
|
EA|S, S|C, // FIMUL long
|
|
EA|S, C, // FICOM long
|
|
EA|S, S|C, // FICOMP long
|
|
EA|S, S|C, // FISUB long
|
|
EA|S, S|C, // FISUBR long
|
|
EA|S, S|C, // FIDIV long
|
|
EA|S, S|C, // FIDIVR long
|
|
|
|
// 0xDB
|
|
EA, S|C, // FILD long
|
|
S, EA|S|C, // FISTTP int
|
|
S, EA|C, // FIST long
|
|
S, EA|S|C, // FISTP long
|
|
N, N, //
|
|
EA, S|C, // FLD real80
|
|
N, N, //
|
|
S, EA|S|C, // FSTP real80
|
|
|
|
// 0xDC
|
|
EA|S, S|C, // FADD double
|
|
EA|S, S|C, // FMUL double
|
|
EA|S, C, // FCOM double
|
|
EA|S, S|C, // FCOMP double
|
|
EA|S, S|C, // FSUB double
|
|
EA|S, S|C, // FSUBR double
|
|
EA|S, S|C, // FDIV double
|
|
EA|S, S|C, // FDIVR double
|
|
|
|
// 0xDD
|
|
EA, S|C, // FLD double
|
|
S, EA|S|C, // FISTTP long
|
|
S, EA|C, // FST double
|
|
S, EA|S|C, // FSTP double
|
|
N, N, // FRSTOR
|
|
N, N, //
|
|
N, N, // FSAVE
|
|
C, EA, // FSTSW
|
|
|
|
// 0xDE
|
|
EA|S, S|C, // FIADD short
|
|
EA|S, S|C, // FIMUL short
|
|
EA|S, C, // FICOM short
|
|
EA|S, S|C, // FICOMP short
|
|
EA|S, S|C, // FISUB short
|
|
EA|S, S|C, // FISUBR short
|
|
EA|S, S|C, // FIDIV short
|
|
EA|S, S|C, // FIDIVR short
|
|
|
|
// 0xDF
|
|
EA, S|C, // FILD short
|
|
S, EA|S|C, // FISTTP short
|
|
S, EA|C, // FIST short
|
|
S, EA|S|C, // FISTP short
|
|
EA, S|C, // FBLD packed BCD
|
|
EA, S|C, // FILD long long
|
|
S, EA|S|C, // FBSTP packed BCD
|
|
S, EA|S|C, // FISTP long long
|
|
};
|
|
|
|
|
|
/********************************************
|
|
* Micro-ops for floating point opcodes 0xD8..0xDF, with Irm < 0xC0.
|
|
*/
|
|
|
|
static unsigned char uopsgrpf1[8][8] =
|
|
{
|
|
// 0xD8
|
|
2, // FADD float
|
|
2, // FMUL float
|
|
2, // FCOM float
|
|
2, // FCOMP float
|
|
2, // FSUB float
|
|
2, // FSUBR float
|
|
2, // FDIV float
|
|
2, // FDIVR float
|
|
|
|
// 0xD9
|
|
1, // FLD float
|
|
0, //
|
|
2, // FST float
|
|
2, // FSTP float
|
|
5, // FLDENV
|
|
3, // FLDCW
|
|
5, // FSTENV
|
|
5, // FSTCW
|
|
|
|
// 0xDA
|
|
5, // FIADD long
|
|
5, // FIMUL long
|
|
5, // FICOM long
|
|
5, // FICOMP long
|
|
5, // FISUB long
|
|
5, // FISUBR long
|
|
5, // FIDIV long
|
|
5, // FIDIVR long
|
|
|
|
// 0xDB
|
|
4, // FILD long
|
|
0, //
|
|
4, // FIST long
|
|
4, // FISTP long
|
|
0, //
|
|
4, // FLD real80
|
|
0, //
|
|
5, // FSTP real80
|
|
|
|
// 0xDC
|
|
2, // FADD double
|
|
2, // FMUL double
|
|
2, // FCOM double
|
|
2, // FCOMP double
|
|
2, // FSUB double
|
|
2, // FSUBR double
|
|
2, // FDIV double
|
|
2, // FDIVR double
|
|
|
|
// 0xDD
|
|
1, // FLD double
|
|
0, //
|
|
2, // FST double
|
|
2, // FSTP double
|
|
5, // FRSTOR
|
|
0, //
|
|
5, // FSAVE
|
|
5, // FSTSW
|
|
|
|
// 0xDE
|
|
5, // FIADD short
|
|
5, // FIMUL short
|
|
5, // FICOM short
|
|
5, // FICOMP short
|
|
5, // FISUB short
|
|
5, // FISUBR short
|
|
5, // FIDIV short
|
|
5, // FIDIVR short
|
|
|
|
// 0xDF
|
|
4, // FILD short
|
|
0, //
|
|
4, // FIST short
|
|
4, // FISTP short
|
|
5, // FBLD packed BCD
|
|
4, // FILD long long
|
|
5, // FBSTP packed BCD
|
|
4, // FISTP long long
|
|
};
|
|
|
|
/**************************************************
|
|
* Determine number of micro-ops for Pentium Pro and Pentium II processors.
|
|
* 0 means special case,
|
|
* 5 means 'complex'
|
|
*/
|
|
|
|
static const unsigned char insuops[256] =
|
|
{ 0,0,0,0, 1,1,4,5, /* 00 */
|
|
0,0,0,0, 1,1,4,0, /* 08 */
|
|
0,0,0,0, 2,2,4,5, /* 10 */
|
|
0,0,0,0, 2,2,4,5, /* 18 */
|
|
0,0,0,0, 1,1,0,1, /* 20 */
|
|
0,0,0,0, 1,1,0,1, /* 28 */
|
|
0,0,0,0, 1,1,0,1, /* 30 */
|
|
0,0,0,0, 1,1,0,1, /* 38 */
|
|
1,1,1,1, 1,1,1,1, /* 40 */
|
|
1,1,1,1, 1,1,1,1, /* 48 */
|
|
3,3,3,3, 3,3,3,3, /* 50 */
|
|
2,2,2,2, 3,2,2,2, /* 58 */
|
|
5,5,5,5, 0,0,0,0, /* 60 */
|
|
3,3,0,0, 5,5,5,5, /* 68 */
|
|
1,1,1,1, 1,1,1,1, /* 70 */
|
|
1,1,1,1, 1,1,1,1, /* 78 */
|
|
0,0,0,0, 0,0,0,0, /* 80 */
|
|
0,0,0,0, 0,1,4,0, /* 88 */
|
|
1,3,3,3, 3,3,3,3, /* 90 */
|
|
1,1,5,0, 5,5,1,1, /* 98 */
|
|
1,1,2,2, 5,5,5,5, /* A0 */
|
|
1,1,3,3, 2,2,3,3, /* A8 */
|
|
1,1,1,1, 1,1,1,1, /* B0 */
|
|
1,1,1,1, 1,1,1,1, /* B8 */
|
|
0,0,5,4, 0,0,0,0, /* C0 */
|
|
5,3,5,5, 5,3,5,5, /* C8 */
|
|
0,0,0,0, 4,3,0,2, /* D0 */
|
|
0,0,0,0, 0,0,0,0, /* D8 */
|
|
4,4,4,2, 5,5,5,5, /* E0 */
|
|
4,1,5,1, 5,5,5,5, /* E8 */
|
|
0,0,5,5, 5,1,0,0, /* F0 */
|
|
1,1,5,5, 4,4,0,0, /* F8 */
|
|
};
|
|
|
|
static unsigned char uopsx[8] = { 1,1,2,5,1,1,1,5 };
|
|
|
|
/************************************************
|
|
* Determine number of micro-ops for Pentium Pro and Pentium II processors.
|
|
* 5 means 'complex'.
|
|
* Doesn't currently handle:
|
|
* floating point
|
|
* MMX
|
|
* 0F opcodes
|
|
* prefix bytes
|
|
*/
|
|
|
|
STATIC int uops(code *c)
|
|
{ int n;
|
|
int op;
|
|
int op2;
|
|
|
|
op = c->Iop & 0xFF;
|
|
if ((c->Iop & 0xFF00) == 0x0F00)
|
|
op = 0x0F;
|
|
n = insuops[op];
|
|
if (!n) // if special case
|
|
{ unsigned char irm,mod,reg,rm;
|
|
|
|
irm = c->Irm;
|
|
mod = (irm >> 6) & 3;
|
|
reg = (irm >> 3) & 7;
|
|
rm = irm & 7;
|
|
|
|
switch (op)
|
|
{
|
|
case 0x10:
|
|
case 0x11: // ADC rm,r
|
|
case 0x18:
|
|
case 0x19: // SBB rm,r
|
|
n = (mod == 3) ? 2 : 4;
|
|
break;
|
|
|
|
case 0x12:
|
|
case 0x13: // ADC r,rm
|
|
case 0x1A:
|
|
case 0x1B: // SBB r,rm
|
|
n = (mod == 3) ? 2 : 3;
|
|
break;
|
|
|
|
case 0x00:
|
|
case 0x01: // ADD rm,r
|
|
case 0x08:
|
|
case 0x09: // OR rm,r
|
|
case 0x20:
|
|
case 0x21: // AND rm,r
|
|
case 0x28:
|
|
case 0x29: // SUB rm,r
|
|
case 0x30:
|
|
case 0x31: // XOR rm,r
|
|
n = (mod == 3) ? 1 : 4;
|
|
break;
|
|
|
|
case 0x02:
|
|
case 0x03: // ADD r,rm
|
|
case 0x0A:
|
|
case 0x0B: // OR r,rm
|
|
case 0x22:
|
|
case 0x23: // AND r,rm
|
|
case 0x2A:
|
|
case 0x2B: // SUB r,rm
|
|
case 0x32:
|
|
case 0x33: // XOR r,rm
|
|
case 0x38:
|
|
case 0x39: // CMP rm,r
|
|
case 0x3A:
|
|
case 0x3B: // CMP r,rm
|
|
case 0x69: // IMUL rm,r,imm
|
|
case 0x6B: // IMUL rm,r,imm8
|
|
case 0x84:
|
|
case 0x85: // TEST rm,r
|
|
n = (mod == 3) ? 1 : 2;
|
|
break;
|
|
|
|
case 0x80:
|
|
case 0x81:
|
|
case 0x82:
|
|
case 0x83:
|
|
if (reg == 2 || reg == 3) // ADC/SBB rm,imm
|
|
n = (mod == 3) ? 2 : 4;
|
|
else if (reg == 7) // CMP rm,imm
|
|
n = (mod == 3) ? 1 : 2;
|
|
else
|
|
n = (mod == 3) ? 1 : 4;
|
|
break;
|
|
|
|
case 0x86:
|
|
case 0x87: // XCHG rm,r
|
|
n = (mod == 3) ? 3 : 5;
|
|
break;
|
|
|
|
case 0x88:
|
|
case 0x89: // MOV rm,r
|
|
n = (mod == 3) ? 1 : 2;
|
|
break;
|
|
|
|
case 0x8A:
|
|
case 0x8B: // MOV r,rm
|
|
n = 1;
|
|
break;
|
|
|
|
case 0x8C: // MOV Sreg,rm
|
|
n = (mod == 3) ? 1 : 3;
|
|
break;
|
|
|
|
case 0x8F:
|
|
if (reg == 0) // POP m
|
|
n = 5;
|
|
break;
|
|
|
|
case 0xC6:
|
|
case 0xC7:
|
|
if (reg == 0) // MOV rm,imm
|
|
n = (mod == 3) ? 1 : 2;
|
|
break;
|
|
|
|
case 0xD0:
|
|
case 0xD1:
|
|
if (reg == 2 || reg == 3) // RCL/RCR rm,1
|
|
n = (mod == 3) ? 2 : 4;
|
|
else
|
|
n = (mod == 3) ? 1 : 4;
|
|
break;
|
|
|
|
case 0xC0:
|
|
case 0xC1: // RCL/RCR rm,imm8
|
|
case 0xD2:
|
|
case 0xD3:
|
|
if (reg == 2 || reg == 3) // RCL/RCR rm,CL
|
|
n = 5;
|
|
else
|
|
n = (mod == 3) ? 1 : 4;
|
|
break;
|
|
|
|
case 0xD8:
|
|
case 0xD9:
|
|
case 0xDA:
|
|
case 0xDB:
|
|
case 0xDC:
|
|
case 0xDD:
|
|
case 0xDE:
|
|
case 0xDF:
|
|
// Floating point opcodes
|
|
if (irm < 0xC0)
|
|
{ n = uopsgrpf1[op - 0xD8][reg];
|
|
break;
|
|
}
|
|
n = uopsx[op - 0xD8];
|
|
switch (op)
|
|
{
|
|
case 0xD9:
|
|
switch (irm)
|
|
{
|
|
case 0xE0: // FCHS
|
|
n = 3;
|
|
break;
|
|
case 0xE8:
|
|
case 0xE9:
|
|
case 0xEA:
|
|
case 0xEB:
|
|
case 0xEC:
|
|
case 0xED:
|
|
n = 2;
|
|
break;
|
|
case 0xF0:
|
|
case 0xF1:
|
|
case 0xF2:
|
|
case 0xF3:
|
|
case 0xF4:
|
|
case 0xF5:
|
|
case 0xF8:
|
|
case 0xF9:
|
|
case 0xFB:
|
|
case 0xFC:
|
|
case 0xFD:
|
|
case 0xFE:
|
|
case 0xFF:
|
|
n = 5;
|
|
break;
|
|
}
|
|
break;
|
|
case 0xDE:
|
|
if (irm == 0xD9) // FCOMPP
|
|
n = 2;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0xF6:
|
|
if (reg == 6 || reg == 7) // DIV AL,rm8
|
|
n = (mod == 3) ? 3 : 4;
|
|
else if (reg == 4 || reg == 5 || reg == 0) // MUL/IMUL/TEST rm8
|
|
n = (mod == 3) ? 1 : 2;
|
|
else if (reg == 2 || reg == 3) // NOT/NEG rm
|
|
n = (mod == 3) ? 1 : 4;
|
|
break;
|
|
|
|
case 0xF7:
|
|
if (reg == 6 || reg == 7) // DIV EAX,rm
|
|
n = 4;
|
|
else if (reg == 4 || reg == 5) // MUL/IMUL rm
|
|
n = (mod == 3) ? 3 : 4;
|
|
else if (reg == 2 || reg == 3) // NOT/NEG rm
|
|
n = (mod == 3) ? 1 : 4;
|
|
break;
|
|
|
|
case 0xFF:
|
|
if (reg == 2 || reg == 3 || // CALL rm, CALL m,rm
|
|
reg == 5) // JMP seg:offset
|
|
n = 5;
|
|
else if (reg == 4)
|
|
n = (mod == 3) ? 1 : 2;
|
|
else if (reg == 0 || reg == 1) // INC/DEC rm
|
|
n = (mod == 3) ? 1 : 4;
|
|
else if (reg == 6) // PUSH rm
|
|
n = (mod == 3) ? 3 : 4;
|
|
break;
|
|
|
|
case 0x0F:
|
|
op2 = c->Iop & 0xFF;
|
|
if ((op2 & 0xF0) == 0x80) // Jcc
|
|
{ n = 1;
|
|
break;
|
|
}
|
|
if ((op2 & 0xF0) == 0x90) // SETcc
|
|
{ n = (mod == 3) ? 1 : 3;
|
|
break;
|
|
}
|
|
if (op2 == 0xB6 || op2 == 0xB7 || // MOVZX
|
|
op2 == 0xBE || op2 == 0xBF) // MOVSX
|
|
{ n = 1;
|
|
break;
|
|
}
|
|
if (op2 == 0xAF) // IMUL r,m
|
|
{ n = (mod == 3) ? 1 : 2;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (n == 0)
|
|
n = 5; // copout for now
|
|
return n;
|
|
}
|
|
|
|
/******************************************
|
|
* Determine pairing classification.
|
|
* Don't deal with floating point, just assume they are all NP (Not Pairable).
|
|
* Returns:
|
|
* NP,UV,PU,PV optionally OR'd with PE
|
|
*/
|
|
|
|
STATIC int pair_class(code *c)
|
|
{ unsigned char op;
|
|
unsigned char irm,mod,reg,rm;
|
|
unsigned a32;
|
|
int pc;
|
|
|
|
// Of course, with Intel this is *never* simple, and Intel's
|
|
// documentation is vague about the specifics.
|
|
|
|
op = c->Iop & 0xFF;
|
|
if ((c->Iop & 0xFF00) == 0x0F00)
|
|
op = 0x0F;
|
|
pc = pentcycl[op];
|
|
a32 = I32;
|
|
if (c->Iflags & CFaddrsize)
|
|
a32 ^= 1;
|
|
irm = c->Irm;
|
|
mod = (irm >> 6) & 3;
|
|
reg = (irm >> 3) & 7;
|
|
rm = irm & 7;
|
|
switch (op)
|
|
{
|
|
case 0x0F: // 2 byte opcode
|
|
if ((c->Iop & 0xF0) == 0x80) // if Jcc
|
|
pc = PV | PF;
|
|
break;
|
|
|
|
case 0x80:
|
|
case 0x81:
|
|
case 0x83:
|
|
if (reg == 2 || // ADC EA,immed
|
|
reg == 3) // SBB EA,immed
|
|
{ pc = PU;
|
|
goto L2;
|
|
}
|
|
goto L1; // AND/OR/XOR/ADD/SUB/CMP EA,immed
|
|
|
|
case 0x84:
|
|
case 0x85: // TEST EA,reg
|
|
if (mod == 3) // TEST reg,reg
|
|
pc = UV;
|
|
break;
|
|
|
|
case 0xC0:
|
|
case 0xC1:
|
|
if (reg >= 4)
|
|
pc = PU;
|
|
break;
|
|
|
|
case 0xC6:
|
|
case 0xC7:
|
|
if (reg == 0) // MOV EA,immed
|
|
{
|
|
L1:
|
|
pc = UV;
|
|
L2:
|
|
// if EA contains a displacement then
|
|
// can't execute in V, or pair in U
|
|
switch (mod)
|
|
{ case 0:
|
|
if (a32)
|
|
{ if (rm == 5 ||
|
|
(rm == 4 && (c->Isib & 7) == 5)
|
|
)
|
|
pc = NP;
|
|
}
|
|
else if (rm == 6)
|
|
pc = NP;
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
pc = NP;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0xD9:
|
|
if (irm < 0xC0)
|
|
{
|
|
if (reg == 0)
|
|
pc = FX;
|
|
}
|
|
else if (irm < 0xC8)
|
|
pc = FX;
|
|
else if (irm < 0xD0)
|
|
pc = PV;
|
|
else
|
|
{
|
|
switch (irm)
|
|
{
|
|
case 0xE0:
|
|
case 0xE1:
|
|
case 0xE4:
|
|
pc = FX;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 0xDB:
|
|
if (irm < 0xC0 && (reg == 0 || reg == 5))
|
|
pc = FX;
|
|
break;
|
|
|
|
case 0xDD:
|
|
if (irm < 0xC0)
|
|
{
|
|
if (reg == 0)
|
|
pc = FX;
|
|
}
|
|
else if (irm >= 0xE0 && irm < 0xF0)
|
|
pc = FX;
|
|
break;
|
|
|
|
case 0xDF:
|
|
if (irm < 0xC0 && (reg == 0 || reg == 5))
|
|
pc = FX;
|
|
break;
|
|
|
|
case 0xFE:
|
|
if (reg == 0 || reg == 1) // INC/DEC EA
|
|
pc = UV;
|
|
break;
|
|
case 0xFF:
|
|
if (reg == 0 || reg == 1) // INC/DEC EA
|
|
pc = UV;
|
|
else if (reg == 2 || reg == 4) // CALL/JMP near ptr EA
|
|
pc = PE|PV;
|
|
else if (reg == 6 && mod == 3) // PUSH reg
|
|
pc = PE | UV;
|
|
break;
|
|
}
|
|
if (c->Iflags & CFPREFIX && pc == UV) // if prefix byte
|
|
pc = PU;
|
|
return pc;
|
|
}
|
|
|
|
/******************************************
|
|
* For an instruction, determine what is read
|
|
* and what is written, and what is used for addressing.
|
|
* Determine operand size if EA (larger is ok).
|
|
*/
|
|
|
|
STATIC void getinfo(Cinfo *ci,code *c)
|
|
{
|
|
memset(ci,0,sizeof(Cinfo));
|
|
if (!c)
|
|
return;
|
|
ci->c = c;
|
|
|
|
if (PRO)
|
|
{
|
|
ci->uops = uops(c);
|
|
ci->isz = calccodsize(c);
|
|
}
|
|
else
|
|
ci->pair = pair_class(c);
|
|
|
|
unsigned char op;
|
|
unsigned char op2;
|
|
unsigned char irm,mod,reg,rm;
|
|
unsigned a32;
|
|
int pc;
|
|
unsigned r,w;
|
|
int sz = I32 ? 4 : 2;
|
|
|
|
ci->r = 0;
|
|
ci->w = 0;
|
|
ci->a = 0;
|
|
op = c->Iop & 0xFF;
|
|
if ((c->Iop & 0xFF00) == 0x0F00)
|
|
op = 0x0F;
|
|
//printf("\tgetinfo %x, op %x \n",c,op);
|
|
pc = pentcycl[op];
|
|
a32 = I32;
|
|
if (c->Iflags & CFaddrsize)
|
|
a32 ^= 1;
|
|
if (c->Iflags & CFopsize)
|
|
sz ^= 2 | 4;
|
|
irm = c->Irm;
|
|
mod = (irm >> 6) & 3;
|
|
reg = (irm >> 3) & 7;
|
|
rm = irm & 7;
|
|
|
|
r = oprw[op][0];
|
|
w = oprw[op][1];
|
|
|
|
switch (op)
|
|
{
|
|
case 0x50:
|
|
case 0x51:
|
|
case 0x52:
|
|
case 0x53:
|
|
case 0x55:
|
|
case 0x56:
|
|
case 0x57: // PUSH reg
|
|
ci->flags |= CIFLpush;
|
|
case 0x54: // PUSH ESP
|
|
case 0x6A: // PUSH imm8
|
|
case 0x68: // PUSH imm
|
|
case 0x0E:
|
|
case 0x16:
|
|
case 0x1E:
|
|
case 0x06:
|
|
case 0x9C:
|
|
Lpush:
|
|
ci->spadjust = -sz;
|
|
ci->a |= mSP;
|
|
break;
|
|
|
|
case 0x58:
|
|
case 0x59:
|
|
case 0x5A:
|
|
case 0x5B:
|
|
case 0x5C:
|
|
case 0x5D:
|
|
case 0x5E:
|
|
case 0x5F: // POP reg
|
|
case 0x1F:
|
|
case 0x07:
|
|
case 0x17:
|
|
case 0x9D: // POPF
|
|
Lpop:
|
|
ci->spadjust = sz;
|
|
ci->a |= mSP;
|
|
break;
|
|
|
|
case 0x80:
|
|
if (reg == 7) // CMP
|
|
c->Iflags |= CFpsw;
|
|
r = B | grprw[0][reg][0]; // Grp 1 (byte)
|
|
w = B | grprw[0][reg][1];
|
|
break;
|
|
|
|
case 0x81:
|
|
case 0x83:
|
|
if (reg == 7) // CMP
|
|
c->Iflags |= CFpsw;
|
|
else if (irm == modregrm(3,0,SP)) // ADD ESP,imm
|
|
{
|
|
assert(c->IFL2 == FLconst);
|
|
ci->spadjust = (op == 0x81) ? c->IEV2.Vint : (signed char)c->IEV2.Vint;
|
|
}
|
|
else if (irm == modregrm(3,5,SP)) // SUB ESP,imm
|
|
{
|
|
assert(c->IFL2 == FLconst);
|
|
ci->spadjust = (op == 0x81) ? -c->IEV2.Vint : -(signed char)c->IEV2.Vint;
|
|
}
|
|
r = grprw[0][reg][0]; // Grp 1
|
|
w = grprw[0][reg][1];
|
|
break;
|
|
|
|
case 0x8F:
|
|
if (reg == 0) // POP rm
|
|
goto Lpop;
|
|
break;
|
|
|
|
case 0xA0:
|
|
case 0xA1:
|
|
case 0xA2:
|
|
case 0xA3:
|
|
// Fake having an EA to simplify code in conflict()
|
|
ci->flags |= CIFLea;
|
|
ci->reg = 0;
|
|
ci->sibmodrm = a32 ? modregrm(0,0,5) : modregrm(0,0,6);
|
|
c->IFL1 = c->IFL2;
|
|
c->IEV1 = c->IEV2;
|
|
break;
|
|
|
|
case 0xC2:
|
|
case 0xC3:
|
|
case 0xCA:
|
|
case 0xCB: // RET
|
|
ci->a |= mSP;
|
|
break;
|
|
|
|
case 0xE8:
|
|
if (c->Iflags & CFclassinit) // call to __j_classinit
|
|
{ r = 0;
|
|
w = F;
|
|
#if CLASSINIT2
|
|
ci->pair = UV; // it is patched to CMP EAX,0
|
|
#else
|
|
ci->pair = NP;
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case 0xF6:
|
|
r = grprw[3][reg][0]; // Grp 3, byte version
|
|
w = grprw[3][reg][1];
|
|
break;
|
|
|
|
case 0xF7:
|
|
r = grprw[1][reg][0]; // Grp 3
|
|
w = grprw[1][reg][1];
|
|
break;
|
|
|
|
case 0x0F:
|
|
op2 = c->Iop & 0xFF;
|
|
if ((op2 & 0xF0) == 0x80) // if Jxx instructions
|
|
{
|
|
ci->r = F | N;
|
|
ci->w = N;
|
|
goto Lret;
|
|
}
|
|
ci->r = N;
|
|
ci->w = N; // copout for now
|
|
goto Lret;
|
|
|
|
case 0xD7: // XLAT
|
|
ci->a = mAX | mBX;
|
|
break;
|
|
|
|
case 0xFF:
|
|
r = grprw[2][reg][0]; // Grp 5
|
|
w = grprw[2][reg][1];
|
|
if (reg == 6) // PUSH rm
|
|
goto Lpush;
|
|
break;
|
|
|
|
case 0x38:
|
|
case 0x39:
|
|
case 0x3A:
|
|
case 0x3B:
|
|
case 0x3C: // CMP AL,imm8
|
|
case 0x3D: // CMP EAX,imm32
|
|
// For CMP opcodes, always test for flags
|
|
c->Iflags |= CFpsw;
|
|
break;
|
|
|
|
case ESCAPE:
|
|
if (c->Iop == (ESCAPE | ESCadjfpu))
|
|
ci->fpuadjust = c->IEV1.Vint;
|
|
break;
|
|
|
|
case 0xD0:
|
|
case 0xD1:
|
|
case 0xD2:
|
|
case 0xD3:
|
|
case 0xC0:
|
|
case 0xC1:
|
|
if (reg == 2 || reg == 3) // if RCL or RCR
|
|
c->Iflags |= CFpsw; // always test for flags
|
|
break;
|
|
|
|
case 0xD8:
|
|
case 0xD9:
|
|
case 0xDA:
|
|
case 0xDB:
|
|
case 0xDC:
|
|
case 0xDD:
|
|
case 0xDE:
|
|
case 0xDF:
|
|
if (irm < 0xC0)
|
|
{ r = grpf1[op - 0xD8][reg][0];
|
|
w = grpf1[op - 0xD8][reg][1];
|
|
switch (op)
|
|
{
|
|
case 0xD8:
|
|
if (reg == 3) // if FCOMP
|
|
ci->fpuadjust = -1;
|
|
else
|
|
ci->fp_op = FPfop;
|
|
break;
|
|
|
|
case 0xD9:
|
|
if (reg == 0) // if FLD float
|
|
{ ci->fpuadjust = 1;
|
|
ci->fp_op = FPfld;
|
|
}
|
|
else if (reg == 3) // if FSTP float
|
|
{ ci->fpuadjust = -1;
|
|
ci->fp_op = FPfstp;
|
|
}
|
|
else if (reg == 5 || reg == 7)
|
|
sz = 2;
|
|
else if (reg == 4 || reg == 6)
|
|
sz = 28;
|
|
break;
|
|
case 0xDA:
|
|
if (reg == 3) // if FICOMP
|
|
ci->fpuadjust = -1;
|
|
break;
|
|
case 0xDB:
|
|
if (reg == 0 || reg == 5)
|
|
{ ci->fpuadjust = 1;
|
|
ci->fp_op = FPfld; // FILD / FLD long double
|
|
}
|
|
if (reg == 3 || reg == 7)
|
|
ci->fpuadjust = -1;
|
|
if (reg == 7)
|
|
ci->fp_op = FPfstp; // FSTP long double
|
|
if (reg == 5 || reg == 7)
|
|
sz = 10;
|
|
break;
|
|
case 0xDC:
|
|
sz = 8;
|
|
if (reg == 3) // if FCOMP
|
|
ci->fpuadjust = -1;
|
|
else
|
|
ci->fp_op = FPfop;
|
|
break;
|
|
case 0xDD:
|
|
if (reg == 0) // if FLD double
|
|
{ ci->fpuadjust = 1;
|
|
ci->fp_op = FPfld;
|
|
}
|
|
if (reg == 3) // if FSTP double
|
|
{ ci->fpuadjust = -1;
|
|
ci->fp_op = FPfstp;
|
|
}
|
|
if (reg == 7)
|
|
sz = 2;
|
|
else if (reg == 4 || reg == 6)
|
|
sz = 108;
|
|
else
|
|
sz = 8;
|
|
break;
|
|
case 0xDE:
|
|
sz = 2;
|
|
if (reg == 3) // if FICOMP
|
|
ci->fpuadjust = -1;
|
|
break;
|
|
case 0xDF:
|
|
sz = 2;
|
|
if (reg == 4 || reg == 6)
|
|
sz = 10;
|
|
else if (reg == 5 || reg == 7)
|
|
sz = 8;
|
|
if (reg == 0 || reg == 4 || reg == 5)
|
|
ci->fpuadjust = 1;
|
|
else if (reg == 3 || reg == 6 || reg == 7)
|
|
ci->fpuadjust = -1;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else if (op == 0xDE)
|
|
{ ci->fpuadjust = -1; // pop versions of Fop's
|
|
if (irm == 0xD9)
|
|
ci->fpuadjust = -2; // FCOMPP
|
|
}
|
|
|
|
// Most floating point opcodes aren't staged, but are
|
|
// sent right through, in order to make use of the large
|
|
// latencies with floating point instructions.
|
|
if (ci->fp_op == FPfld ||
|
|
(op == 0xD9 && (irm & 0xF8) == 0xC0))
|
|
; // FLD ST(i)
|
|
else
|
|
ci->flags |= CIFLnostage;
|
|
|
|
switch (op)
|
|
{
|
|
case 0xD8:
|
|
r = S;
|
|
w = C;
|
|
if ((irm & ~7) == 0xD0)
|
|
w |= S;
|
|
break;
|
|
case 0xD9:
|
|
// FCHS or FABS or FSQRT
|
|
if (irm == 0xE0 || irm == 0xE1 || irm == 0xFA)
|
|
ci->fp_op = FPfop;
|
|
r = S;
|
|
w = S|C;
|
|
break;
|
|
case 0xDA:
|
|
if (irm == 0xE9) // FUCOMPP
|
|
{ r = S;
|
|
w = S|C;
|
|
break;
|
|
}
|
|
break;
|
|
case 0xDB:
|
|
if (irm == 0xE2) // FCLEX
|
|
{ r = 0;
|
|
w = C;
|
|
break;
|
|
}
|
|
if (irm == 0xE3) // FINIT
|
|
{ r = 0;
|
|
w = S|C;
|
|
break;
|
|
}
|
|
break;
|
|
case 0xDC:
|
|
case 0xDE:
|
|
if ((irm & 0xF0) != 0xD0)
|
|
{ r = S;
|
|
w = S|C;
|
|
break;
|
|
}
|
|
break;
|
|
case 0xDD:
|
|
// Not entirely correct, but conservative
|
|
r = S;
|
|
w = S|C;
|
|
break;
|
|
case 0xDF:
|
|
if (irm == 0xE0) // FSTSW AX
|
|
{ r = C;
|
|
w = mAX;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
#if DEBUG
|
|
default:
|
|
//printf("\t\tNo special case\n");
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if ((r | w) & B) // if byte operation
|
|
sz = 1; // operand size is 1
|
|
|
|
ci->r = r & ~(R | EA);
|
|
ci->w = w & ~(R | EA);
|
|
if (r & R)
|
|
ci->r |= mask[(r & B) ? (reg & 3) : reg];
|
|
if (w & R)
|
|
ci->w |= mask[(w & B) ? (reg & 3) : reg];
|
|
|
|
// OR in bits for EA addressing mode
|
|
if ((r | w) & EA)
|
|
{ unsigned char sib;
|
|
|
|
sib = 0;
|
|
switch (mod)
|
|
{
|
|
case 0:
|
|
if (a32)
|
|
{
|
|
if (rm == 4)
|
|
{ sib = c->Isib;
|
|
if ((sib & modregrm(0,7,0)) != modregrm(0,4,0))
|
|
ci->a |= mask[(sib >> 3) & 7]; // index register
|
|
if ((sib & 7) != 5)
|
|
ci->a |= mask[sib & 7]; // base register
|
|
}
|
|
else if (rm != 5)
|
|
ci->a |= mask[rm];
|
|
}
|
|
else
|
|
{ static unsigned char ea16[8] = {mBX|mSI,mBX|mDI,mBP|mSI,mBP|mDI,mSI,mDI,0,mBX};
|
|
ci->a |= ea16[rm];
|
|
}
|
|
goto Lmem;
|
|
|
|
case 1:
|
|
case 2:
|
|
if (a32)
|
|
{
|
|
if (rm == 4)
|
|
{ sib = c->Isib;
|
|
if ((sib & modregrm(0,7,0)) != modregrm(0,4,0))
|
|
ci->a |= mask[(sib >> 3) & 7]; // index register
|
|
ci->a |= mask[sib & 7]; // base register
|
|
}
|
|
else
|
|
ci->a |= mask[rm];
|
|
}
|
|
else
|
|
{ static unsigned char ea16[8] = {mBX|mSI,mBX|mDI,mBP|mSI,mBP|mDI,mSI,mDI,mBP,mBX};
|
|
ci->a |= ea16[rm];
|
|
}
|
|
|
|
Lmem:
|
|
if (r & EA)
|
|
ci->r |= mMEM;
|
|
if (w & EA)
|
|
ci->w |= mMEM;
|
|
ci->flags |= CIFLea;
|
|
break;
|
|
|
|
case 3:
|
|
if (r & EA)
|
|
ci->r |= mask[(r & B) ? (rm & 3) : rm];
|
|
if (w & EA)
|
|
ci->w |= mask[(w & B) ? (rm & 3) : rm];
|
|
break;
|
|
}
|
|
// Adjust sibmodrm so that addressing modes can be compared simply
|
|
irm &= modregrm(3,0,7);
|
|
if (a32)
|
|
{
|
|
if (irm != modregrm(0,0,5))
|
|
{
|
|
switch (mod)
|
|
{ case 0:
|
|
if ((sib & 7) != 5) // if not disp32[index]
|
|
{ c->IFL1 = FLconst;
|
|
c->IEVpointer1 = 0;
|
|
irm |= 0x80;
|
|
}
|
|
break;
|
|
case 1:
|
|
c->IEVpointer1 = (signed char) c->IEVpointer1;
|
|
irm = modregrm(2,0,rm);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (irm != modregrm(0,0,6))
|
|
{
|
|
switch (mod)
|
|
{ case 0:
|
|
c->IFL1 = FLconst;
|
|
c->IEVpointer1 = 0;
|
|
irm |= 0x80;
|
|
break;
|
|
case 1:
|
|
c->IEVpointer1 = (signed char) c->IEVpointer1;
|
|
irm = modregrm(2,0,rm);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ci->r |= ci->a;
|
|
ci->reg = reg;
|
|
ci->sibmodrm = (sib << 8) | irm;
|
|
}
|
|
Lret:
|
|
if (ci->w & mSP) // if stack pointer is modified
|
|
ci->w |= mMEM; // then we are implicitly writing to memory
|
|
if (op == 0x8D) // if LEA
|
|
ci->r &= ~mMEM; // memory is not actually read
|
|
ci->sz = sz;
|
|
#if DEBUG
|
|
//printf("\t\t"); ci->print();
|
|
#endif
|
|
}
|
|
|
|
/******************************************
|
|
* Determine if two instructions can pair.
|
|
* Assume that in general, cu can pair in the U pipe and cv in the V.
|
|
* Look for things like register contentions.
|
|
* Input:
|
|
* cu instruction for U pipe
|
|
* cv instruction for V pipe
|
|
* Returns:
|
|
* !=0 if they can pair
|
|
*/
|
|
|
|
STATIC int pair_test(Cinfo *cu,Cinfo *cv)
|
|
{ unsigned pcu;
|
|
unsigned pcv;
|
|
unsigned r1,w1;
|
|
unsigned r2,w2;
|
|
unsigned x;
|
|
|
|
pcu = cu->pair;
|
|
if (!(pcu & PU))
|
|
{
|
|
// See if pairs with FXCH and cv is FXCH
|
|
if (pcu & FX && cv->c->Iop == 0xD9 && (cv->c->Irm & ~7) == 0xC8)
|
|
goto Lpair;
|
|
goto Lnopair;
|
|
}
|
|
pcv = cv->pair;
|
|
if (!(pcv & PV))
|
|
goto Lnopair;
|
|
|
|
r1 = cu->r;
|
|
w1 = cu->w;
|
|
r2 = cv->r;
|
|
w2 = cv->w;
|
|
|
|
x = w1 & (r2 | w2) & ~(F|mMEM); // register contention
|
|
if (x && // if register contention
|
|
!(x == mSP && pcu & pcv & PE) // and not exception
|
|
)
|
|
goto Lnopair;
|
|
|
|
// Look for flags contention
|
|
if (w1 & r2 & F && !(pcv & PF))
|
|
goto Lnopair;
|
|
|
|
Lpair:
|
|
return 1;
|
|
|
|
Lnopair:
|
|
return 0;
|
|
}
|
|
|
|
/******************************************
|
|
* Determine if two instructions have an AGI or register contention.
|
|
* Returns:
|
|
* !=0 if they have an AGI
|
|
*/
|
|
|
|
STATIC int pair_agi(Cinfo *c1,Cinfo *c2)
|
|
{ unsigned x;
|
|
|
|
x = c1->w & c2->a;
|
|
return x && !(x == mSP && c1->pair & c2->pair & PE);
|
|
}
|
|
|
|
/********************************************
|
|
* Determine if three instructions can decode simultaneously
|
|
* in Pentium Pro and Pentium II.
|
|
* Input:
|
|
* c0,c1,c2 candidates for decoders 0,1,2
|
|
* c2 can be NULL
|
|
* Returns:
|
|
* !=0 if they can decode simultaneously
|
|
*/
|
|
|
|
STATIC int triple_test(Cinfo *c0,Cinfo *c1,Cinfo *c2)
|
|
{ int c2isz;
|
|
|
|
assert(c0);
|
|
if (!c1)
|
|
goto Lnopair;
|
|
c2isz = c2 ? c2->isz : 0;
|
|
if (c0->isz > 7 || c1->isz > 7 || c2isz > 7 ||
|
|
c0->isz + c1->isz + c2isz > 16)
|
|
goto Lnopair;
|
|
|
|
// 4-1-1 decode
|
|
if (c1->uops > 1 ||
|
|
(c2 && c2->uops > 1))
|
|
goto Lnopair;
|
|
|
|
Lpair:
|
|
return 1;
|
|
|
|
Lnopair:
|
|
return 0;
|
|
}
|
|
|
|
/********************************************
|
|
* Get next instruction worth looking at for scheduling.
|
|
* Returns:
|
|
* NULL no more instructions
|
|
*/
|
|
|
|
STATIC code * cnext(code *c)
|
|
{
|
|
while (1)
|
|
{
|
|
c = code_next(c);
|
|
if (!c)
|
|
break;
|
|
if (c->Iflags & (CFtarg | CFtarg2))
|
|
break;
|
|
if (!(c->Iop == NOP ||
|
|
c->Iop == (ESCAPE | ESClinnum)))
|
|
break;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/******************************************
|
|
* Instruction scheduler.
|
|
* Input:
|
|
* c list of instructions to schedule
|
|
* scratch scratch registers we can use
|
|
* Returns:
|
|
* revised list of scheduled instructions
|
|
*/
|
|
|
|
///////////////////////////////////
|
|
// Determine if c1 and c2 are swappable.
|
|
// c1 comes before c2.
|
|
// If they do not conflict
|
|
// return 0
|
|
// If they do conflict
|
|
// return 0x100 + delay_clocks
|
|
// Input:
|
|
// fpsched if 1, then adjust fxch_pre and fxch_post to swap,
|
|
// then return 0
|
|
// if 2, then adjust ci1 as well as ci2
|
|
|
|
STATIC int conflict(Cinfo *ci1,Cinfo *ci2,int fpsched)
|
|
{
|
|
code *c1;
|
|
code *c2;
|
|
unsigned r1,w1,a1;
|
|
unsigned r2,w2,a2;
|
|
int sz1,sz2;
|
|
int i = 0;
|
|
int delay_clocks;
|
|
|
|
c1 = ci1->c;
|
|
c2 = ci2->c;
|
|
|
|
//printf("conflict %x %x\n",c1,c2);
|
|
|
|
r1 = ci1->r;
|
|
w1 = ci1->w;
|
|
a1 = ci1->a;
|
|
sz1 = ci1->sz;
|
|
|
|
r2 = ci2->r;
|
|
w2 = ci2->w;
|
|
a2 = ci2->a;
|
|
sz2 = ci2->sz;
|
|
|
|
//printf("r1 %lx w1 %lx a1 %lx sz1 %x\n",r1,w1,a1,sz1);
|
|
//printf("r2 %lx w2 %lx a2 %lx sz2 %x\n",r2,w2,a2,sz2);
|
|
|
|
if ((c1->Iflags | c2->Iflags) & CFvolatile)
|
|
goto Lconflict;
|
|
|
|
// Determine if we should handle FPU register conflicts separately
|
|
//if (fpsched) printf("fp_op %d,%d:\n",ci1->fp_op,ci2->fp_op);
|
|
if (fpsched && ci1->fp_op && ci2->fp_op)
|
|
{
|
|
w1 &= ~(S|C);
|
|
r1 &= ~(S|C);
|
|
w2 &= ~(S|C);
|
|
r2 &= ~(S|C);
|
|
}
|
|
else
|
|
fpsched = 0;
|
|
|
|
if ((r1 | r2) & N)
|
|
{
|
|
goto Lconflict;
|
|
}
|
|
|
|
#if 0
|
|
if (c1->Iop == 0xFF && c2->Iop == 0x8B)
|
|
{ c1->print(); c2->print(); i = 1;
|
|
printf("r1=%lx, w1=%lx, a1=%lx, sz1=%d, r2=%lx, w2=%lx, a2=%lx, sz2=%d\n",r1,w1,a1,sz1,r2,w2,a2,sz2);
|
|
}
|
|
#endif
|
|
L1:
|
|
if (w1 & r2 || (r1 | w1) & w2)
|
|
{ unsigned char ifl1,ifl2;
|
|
|
|
if (i) printf("test\n");
|
|
|
|
#if 0
|
|
if (c1->IFL1 != c2->IFL1) printf("t1\n");
|
|
if ((c1->Irm & modregrm(3,0,7)) != (c2->Irm & modregrm(3,0,7))) printf("t2\n");
|
|
if ((issib(c1->Irm) && c1->Isib != c2->Isib)) printf("t3\n");
|
|
if (c1->IEVpointer1 + sz1 <= c2->IEVpointer1) printf("t4\n");
|
|
if (c2->IEVpointer1 + sz2 <= c1->IEVpointer1) printf("t5\n");
|
|
#endif
|
|
|
|
#if 1 // make sure CFpsw is reliably set
|
|
if (w1 & w2 & F && // if both instructions write to flags
|
|
w1 != F &&
|
|
w2 != F &&
|
|
!((r1 | r2) & F) && // but neither instruction reads them
|
|
!((c1->Iflags | c2->Iflags) & CFpsw)) // and we don't care about flags
|
|
{
|
|
w1 &= ~F;
|
|
w2 &= ~F; // remove conflict
|
|
goto L1; // and try again
|
|
}
|
|
#endif
|
|
// If other than the memory reference is a conflict
|
|
if (w1 & r2 & ~mMEM || (r1 | w1) & w2 & ~mMEM)
|
|
{ if (i) printf("\t1\n");
|
|
if (i) printf("r1=%x, w1=%x, a1=%x, sz1=%d, r2=%x, w2=%x, a2=%x, sz2=%d\n",r1,w1,a1,sz1,r2,w2,a2,sz2);
|
|
goto Lconflict;
|
|
}
|
|
|
|
// If referring to distinct types, then no dependency
|
|
if (c1->Irex && c2->Irex && c1->Irex != c2->Irex)
|
|
goto Lswap;
|
|
|
|
ifl1 = c1->IFL1;
|
|
ifl2 = c2->IFL1;
|
|
|
|
// Special case: Allow indexed references using registers other than
|
|
// ESP and EBP to be swapped with PUSH instructions
|
|
if (((c1->Iop & ~7) == 0x50 || // PUSH reg
|
|
c1->Iop == 0x6A || // PUSH imm8
|
|
c1->Iop == 0x68 || // PUSH imm16/imm32
|
|
(c1->Iop == 0xFF && ci1->reg == 6) // PUSH EA
|
|
) &&
|
|
ci2->flags & CIFLea && !(a2 & mSP) &&
|
|
!(a2 & mBP && (long)c2->IEVpointer1 < 0)
|
|
)
|
|
{
|
|
if (c1->Iop == 0xFF)
|
|
{
|
|
if (!(w2 & mMEM))
|
|
goto Lswap;
|
|
}
|
|
else
|
|
goto Lswap;
|
|
}
|
|
|
|
// Special case: Allow indexed references using registers other than
|
|
// ESP and EBP to be swapped with PUSH instructions
|
|
if (((c2->Iop & ~7) == 0x50 || // PUSH reg
|
|
c2->Iop == 0x6A || // PUSH imm8
|
|
c2->Iop == 0x68 || // PUSH imm16/imm32
|
|
(c2->Iop == 0xFF && ci2->reg == 6) // PUSH EA
|
|
) &&
|
|
ci1->flags & CIFLea && !(a1 & mSP) &&
|
|
!(a2 & mBP && (long)c2->IEVpointer1 < 0)
|
|
)
|
|
{
|
|
if (c2->Iop == 0xFF)
|
|
{
|
|
if (!(w1 & mMEM))
|
|
goto Lswap;
|
|
}
|
|
else
|
|
goto Lswap;
|
|
}
|
|
|
|
// If not both an EA addressing mode, conflict
|
|
if (!(ci1->flags & ci2->flags & CIFLea))
|
|
{ if (i) printf("\t2\n");
|
|
goto Lconflict;
|
|
}
|
|
|
|
if (ci1->sibmodrm == ci2->sibmodrm)
|
|
{ if (ifl1 != ifl2)
|
|
goto Lswap;
|
|
switch (ifl1)
|
|
{
|
|
case FLconst:
|
|
if (c1->IEV1.Vint != c2->IEV1.Vint &&
|
|
(c1->IEV1.Vint + sz1 <= c2->IEV1.Vint ||
|
|
c2->IEV1.Vint + sz2 <= c1->IEV1.Vint))
|
|
goto Lswap;
|
|
break;
|
|
case FLdatseg:
|
|
if (c1->IEVseg1 != c2->IEVseg1 ||
|
|
c1->IEV1.Vint + sz1 <= c2->IEV1.Vint ||
|
|
c2->IEV1.Vint + sz2 <= c1->IEV1.Vint)
|
|
goto Lswap;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((c1->Iflags | c2->Iflags) & CFunambig &&
|
|
(ifl1 != ifl2 ||
|
|
ci1->sibmodrm != ci2->sibmodrm ||
|
|
(c1->IEV1.Vint != c2->IEV1.Vint &&
|
|
(c1->IEV1.Vint + sz1 <= c2->IEV1.Vint ||
|
|
c2->IEV1.Vint + sz2 <= c1->IEV1.Vint)
|
|
)
|
|
)
|
|
)
|
|
{
|
|
// Assume that [EBP] and [ESP] can point to the same location
|
|
if (((a1 | a2) & (mBP | mSP)) == (mBP | mSP))
|
|
goto Lconflict;
|
|
goto Lswap;
|
|
}
|
|
|
|
if (i) printf("\t3\n");
|
|
goto Lconflict;
|
|
}
|
|
|
|
Lswap:
|
|
if (fpsched)
|
|
{ unsigned char a1,b1;
|
|
unsigned char a2,b2;
|
|
|
|
//printf("\tfpsched %d,%d:\n",ci1->fp_op,ci2->fp_op);
|
|
a1 = ci1->fxch_pre;
|
|
b1 = ci1->fxch_post;
|
|
a2 = ci2->fxch_pre;
|
|
b2 = ci2->fxch_post;
|
|
|
|
#define X(a,b) ((a << 8) | b)
|
|
switch (X(ci1->fp_op,ci2->fp_op))
|
|
{
|
|
case X(FPfstp,FPfld):
|
|
if (a1 || b1)
|
|
goto Lconflict;
|
|
if (a2)
|
|
goto Lconflict;
|
|
if (b2 == 0)
|
|
ci2->fxch_post++;
|
|
else if (b2 == 1)
|
|
{
|
|
ci2->fxch_pre++;
|
|
ci2->fxch_post++;
|
|
}
|
|
else
|
|
{
|
|
goto Lconflict;
|
|
}
|
|
break;
|
|
|
|
case X(FPfstp,FPfop):
|
|
if (a1 || b1)
|
|
goto Lconflict;
|
|
ci2->fxch_pre++;
|
|
ci2->fxch_post++;
|
|
break;
|
|
|
|
case X(FPfop,FPfop):
|
|
if (a1 == 0 && b1 == 1 && a2 == 0 && b2 == 0)
|
|
{ ci2->fxch_pre = 1;
|
|
ci2->fxch_post = 1;
|
|
break;
|
|
}
|
|
if (a1 == 0 && b1 == 0 && a2 == 1 && b2 == 1)
|
|
break;
|
|
goto Lconflict;
|
|
|
|
case X(FPfop,FPfld):
|
|
if (a1 || b1)
|
|
goto Lconflict;
|
|
if (a2)
|
|
goto Lconflict;
|
|
if (b2)
|
|
break;
|
|
else if (fpsched == 2)
|
|
ci1->fxch_post = 1;
|
|
ci2->fxch_post = 1;
|
|
break;
|
|
|
|
default:
|
|
goto Lconflict;
|
|
}
|
|
#undef X
|
|
//printf("\tpre = %d, post = %d\n",ci2->fxch_pre,ci2->fxch_post);
|
|
}
|
|
|
|
//printf("w1 = x%x, w2 = x%x\n",w1,w2);
|
|
if (i) printf("no conflict\n\n");
|
|
return 0;
|
|
|
|
Lconflict:
|
|
//printf("r1=%x, w1=%x, r2=%x, w2=%x\n",r1,w1,r2,w2);
|
|
delay_clocks = 0;
|
|
|
|
// Determine if AGI
|
|
if (!PRO && pair_agi(ci1,ci2))
|
|
delay_clocks = 1;
|
|
|
|
// Special delays for floating point
|
|
if (fpsched)
|
|
{ if (ci1->fp_op == FPfld && ci2->fp_op == FPfstp)
|
|
delay_clocks = 1;
|
|
else if (ci1->fp_op == FPfop && ci2->fp_op == FPfstp)
|
|
delay_clocks = 3;
|
|
else if (ci1->fp_op == FPfop && ci2->fp_op == FPfop)
|
|
delay_clocks = 2;
|
|
}
|
|
else if (PRO)
|
|
{
|
|
// Look for partial register write stalls
|
|
if (w1 & r2 & ALLREGS && sz1 < sz2)
|
|
delay_clocks = 7;
|
|
}
|
|
else if ((w1 | r1) & (w2 | r2) & (C | S))
|
|
{ int reg;
|
|
int op;
|
|
|
|
op = c1->Iop;
|
|
reg = c1->Irm & modregrm(0,7,0);
|
|
if (ci1->fp_op == FPfld ||
|
|
(op == 0xD9 && (c1->Irm & 0xF8) == 0xC0)
|
|
)
|
|
; // FLD
|
|
else if (op == 0xD9 && (c1->Irm & 0xF8) == 0xC8)
|
|
; // FXCH
|
|
else if (c2->Iop == 0xD9 && (c2->Irm & 0xF8) == 0xC8)
|
|
; // FXCH
|
|
else
|
|
delay_clocks = 3;
|
|
}
|
|
|
|
if (i) printf("conflict %d\n\n",delay_clocks);
|
|
return 0x100 + delay_clocks;
|
|
}
|
|
|
|
struct Schedule
|
|
{
|
|
#define TBLMAX (2*3*20) // must be divisible by both 2 and 3
|
|
// (U,V pipe in Pentium, 3 decode units
|
|
// in Pentium Pro)
|
|
|
|
Cinfo *tbl[TBLMAX]; // even numbers are U pipe, odd numbers are V
|
|
int tblmax; // max number of slots used
|
|
|
|
Cinfo cinfo[TBLMAX];
|
|
int cinfomax;
|
|
|
|
list_t stagelist; // list of instructions in staging area
|
|
|
|
int fpustackused; // number of slots in FPU stack that are used
|
|
|
|
void initialize(int fpustackinit); // initialize scheduler
|
|
int stage(code *c); // stage instruction
|
|
int insert(Cinfo *ci); // insert c into schedule
|
|
code **assemble(code **pc); // reassemble scheduled instructions
|
|
};
|
|
|
|
/******************************
|
|
*/
|
|
|
|
void Schedule::initialize(int fpustackinit)
|
|
{
|
|
//printf("Schedule::initialize(fpustackinit = %d)\n", fpustackinit);
|
|
memset(this,0,sizeof(Schedule));
|
|
fpustackused = fpustackinit;
|
|
}
|
|
|
|
/******************************
|
|
*/
|
|
|
|
code **Schedule::assemble(code **pc)
|
|
{ int i;
|
|
list_t l;
|
|
code *c;
|
|
|
|
#ifdef DEBUG
|
|
if (debugs) printf("assemble:\n");
|
|
#endif
|
|
assert(!*pc);
|
|
|
|
// Try to insert the rest of the staged instructions
|
|
for (l = stagelist; l; l = list_next(l))
|
|
{ Cinfo *ci;
|
|
|
|
ci = (Cinfo *)list_ptr(l);
|
|
if (!insert(ci))
|
|
break;
|
|
}
|
|
|
|
// Get the instructions out of the schedule table
|
|
assert((unsigned)tblmax <= TBLMAX);
|
|
for (i = 0; i < tblmax; i++)
|
|
{ Cinfo *ci;
|
|
|
|
ci = tbl[i];
|
|
#ifdef DEBUG
|
|
if (debugs)
|
|
{
|
|
if (PRO)
|
|
{ static char tbl[3][4] = { "0 "," 1 "," 2" };
|
|
|
|
if (ci)
|
|
printf("%s %d ",tbl[i - ((i / 3) * 3)],ci->uops);
|
|
else
|
|
printf("%s ",tbl[i - ((i / 3) * 3)]);
|
|
}
|
|
else
|
|
{
|
|
printf((i & 1) ? " V " : "U ");
|
|
}
|
|
if (ci)
|
|
ci->c->print();
|
|
else
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
if (!ci)
|
|
continue;
|
|
fpustackused += ci->fpuadjust;
|
|
//printf("stage()1: fpustackused = %d\n", fpustackused);
|
|
c = ci->c;
|
|
if (i == 0)
|
|
c->Iflags |= CFtarg; // by definition, first is always a jump target
|
|
else
|
|
c->Iflags &= ~CFtarg; // the rest are not
|
|
|
|
// Put in any FXCH prefix
|
|
if (ci->fxch_pre)
|
|
{ code *cf;
|
|
assert(i);
|
|
cf = gen2(NULL,0xD9,0xC8 + ci->fxch_pre);
|
|
*pc = cf;
|
|
pc = &code_next(cf);
|
|
}
|
|
|
|
*pc = c;
|
|
do
|
|
{
|
|
assert(*pc != code_next(*pc));
|
|
pc = &code_next(*pc);
|
|
} while (*pc);
|
|
|
|
// Put in any FXCH postfix
|
|
if (ci->fxch_post)
|
|
{ int j;
|
|
|
|
for (j = i + 1; j < tblmax; j++)
|
|
{ if (tbl[j])
|
|
{ if (tbl[j]->fxch_pre == ci->fxch_post)
|
|
{
|
|
tbl[j]->fxch_pre = 0; // they cancel each other out
|
|
goto L1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
{ code *cf;
|
|
cf = gen2(NULL,0xD9,0xC8 + ci->fxch_post);
|
|
*pc = cf;
|
|
pc = &code_next(cf);
|
|
}
|
|
}
|
|
L1: ;
|
|
}
|
|
|
|
// Just append any instructions left in the staging area
|
|
for (; l; l = list_next(l))
|
|
{ Cinfo *ci = (Cinfo *)list_ptr(l);
|
|
code *c = ci->c;
|
|
|
|
#ifdef DEBUG
|
|
if (debugs) { printf("appending: "); c->print(); }
|
|
#endif
|
|
*pc = c;
|
|
do
|
|
{
|
|
pc = &code_next(*pc);
|
|
|
|
} while (*pc);
|
|
fpustackused += ci->fpuadjust;
|
|
//printf("stage()2: fpustackused = %d\n", fpustackused);
|
|
}
|
|
list_free(&stagelist);
|
|
|
|
return pc;
|
|
}
|
|
|
|
/******************************
|
|
* Insert c into scheduling table.
|
|
* Returns:
|
|
* 0 could not be scheduled; have to start a new one
|
|
*/
|
|
|
|
int Schedule::insert(Cinfo *ci)
|
|
{ code *c;
|
|
int clocks;
|
|
int i;
|
|
int ic = 0;
|
|
int imin;
|
|
targ_size_t offset;
|
|
targ_size_t vpointer;
|
|
int movesp = 0;
|
|
int reg2 = -1; // avoid "may be uninitialized" warning
|
|
|
|
//printf("insert "); ci->c->print();
|
|
//printf("insert() %d\n", fpustackused);
|
|
c = ci->c;
|
|
//printf("\tc->Iop %x\n",c->Iop);
|
|
vpointer = c->IEVpointer1;
|
|
assert((unsigned)tblmax <= TBLMAX);
|
|
if (tblmax == TBLMAX) // if out of space
|
|
goto Lnoinsert;
|
|
if (tblmax == 0) // if table is empty
|
|
{ // Just stuff it in the first slot
|
|
i = tblmax;
|
|
goto Linsert;
|
|
}
|
|
else if (c->Iflags & (CFtarg | CFtarg2))
|
|
// Jump targets can only be first in the scheduler
|
|
goto Lnoinsert;
|
|
|
|
// Special case of:
|
|
// PUSH reg1
|
|
// MOV reg2,x[ESP]
|
|
if (c->Iop == 0x8B &&
|
|
(c->Irm & modregrm(3,0,7)) == modregrm(1,0,4) &&
|
|
c->Isib == modregrm(0,4,SP) &&
|
|
c->IFL1 == FLconst &&
|
|
((signed char)c->IEVpointer1) >= REGSIZE
|
|
)
|
|
{
|
|
movesp = 1; // this is a MOV reg2,offset[ESP]
|
|
offset = (signed char)c->IEVpointer1;
|
|
reg2 = (c->Irm >> 3) & 7;
|
|
}
|
|
|
|
|
|
// Start at tblmax, and back up until we get a conflict
|
|
ic = -1;
|
|
imin = 0;
|
|
for (i = tblmax; i >= 0; i--)
|
|
{ Cinfo *cit;
|
|
|
|
cit = tbl[i];
|
|
if (!cit)
|
|
continue;
|
|
|
|
// Look for special case swap
|
|
if (movesp &&
|
|
(cit->c->Iop & ~7) == 0x50 && // if PUSH reg1
|
|
(cit->c->Iop & 7) != reg2 && // if reg1 != reg2
|
|
((signed char)c->IEVpointer1) >= -cit->spadjust
|
|
)
|
|
{
|
|
c->IEVpointer1 += cit->spadjust;
|
|
//printf("\t1, spadjust = %d, ptr = x%x\n",cit->spadjust,c->IEVpointer1);
|
|
continue;
|
|
}
|
|
|
|
if (movesp &&
|
|
cit->c->Iop == 0x83 &&
|
|
cit->c->Irm == modregrm(3,5,SP) && // if SUB ESP,offset
|
|
cit->c->IFL2 == FLconst &&
|
|
((signed char)c->IEVpointer1) >= -cit->spadjust
|
|
)
|
|
{
|
|
//printf("\t2, spadjust = %d\n",cit->spadjust);
|
|
c->IEVpointer1 += cit->spadjust;
|
|
continue;
|
|
}
|
|
|
|
clocks = conflict(cit,ci,1);
|
|
if (clocks)
|
|
{ int j;
|
|
|
|
ic = i; // where the conflict occurred
|
|
clocks &= 0xFF; // convert to delay count
|
|
|
|
// Move forward the delay clocks
|
|
if (clocks == 0)
|
|
j = i + 1;
|
|
else if (PRO)
|
|
j = (((i + 3) / 3) * 3) + clocks * 3;
|
|
else
|
|
{ j = ((i + 2) & ~1) + clocks * 2;
|
|
|
|
// It's possible we skipped over some AGI generating
|
|
// instructions due to movesp.
|
|
int k;
|
|
for (k = i + 1; k < j; k++)
|
|
{
|
|
if (k >= TBLMAX)
|
|
goto Lnoinsert;
|
|
if (tbl[k] && pair_agi(tbl[k],ci))
|
|
{
|
|
k = ((k + 2) & ~1) + 1;
|
|
}
|
|
}
|
|
j = k;
|
|
}
|
|
|
|
if (j >= TBLMAX) // exceed table size?
|
|
goto Lnoinsert;
|
|
imin = j; // first possible slot c can go in
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Scan forward looking for a hole to put it in
|
|
for (i = imin; i < TBLMAX; i++)
|
|
{
|
|
if (tbl[i])
|
|
{
|
|
// In case, due to movesp, we skipped over some AGI instructions
|
|
if (!PRO && pair_agi(tbl[i],ci))
|
|
{
|
|
i = ((i + 2) & ~1) + 1;
|
|
if (i >= TBLMAX)
|
|
goto Lnoinsert;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PRO)
|
|
{ int i0 = (i / 3) * 3; // index of decode unit 0
|
|
Cinfo *ci0;
|
|
|
|
assert(((TBLMAX / 3) * 3) == TBLMAX);
|
|
switch (i - i0)
|
|
{
|
|
case 0: // i0 can handle any instruction
|
|
goto Linsert;
|
|
case 1:
|
|
ci0 = tbl[i0];
|
|
if (ci->uops > 1)
|
|
{
|
|
if (i0 >= imin && ci0->uops == 1)
|
|
goto L1;
|
|
i++;
|
|
break;
|
|
}
|
|
if (triple_test(ci0,ci,tbl[i0 + 2]))
|
|
goto Linsert;
|
|
break;
|
|
case 2:
|
|
ci0 = tbl[i0];
|
|
if (ci->uops > 1)
|
|
{
|
|
if (i0 >= imin && ci0->uops == 1)
|
|
{
|
|
if (i >= tblmax)
|
|
{ if (i + 1 >= TBLMAX)
|
|
goto Lnoinsert;
|
|
tblmax = i + 1;
|
|
}
|
|
tbl[i0 + 2] = tbl[i0 + 1];
|
|
tbl[i0 + 1] = ci0;
|
|
i = i0;
|
|
goto Linsert;
|
|
}
|
|
break;
|
|
}
|
|
if (triple_test(ci0,tbl[i0 + 1],ci))
|
|
goto Linsert;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert((TBLMAX & 1) == 0);
|
|
if (i & 1) // if V pipe
|
|
{
|
|
if (pair_test(tbl[i - 1],ci))
|
|
{
|
|
goto Linsert;
|
|
}
|
|
else if (i > imin && pair_test(ci,tbl[i - 1]))
|
|
{
|
|
L1:
|
|
tbl[i] = tbl[i - 1];
|
|
if (i >= tblmax)
|
|
tblmax = i + 1;
|
|
i--;
|
|
//printf("\tswapping with x%02x\n",tbl[i + 1]->c->Iop);
|
|
goto Linsert;
|
|
}
|
|
}
|
|
else // will always fit in U pipe
|
|
{
|
|
assert(!tbl[i + 1]); // because V pipe should be empty
|
|
goto Linsert;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Lnoinsert:
|
|
//printf("\tnoinsert\n");
|
|
c->IEVpointer1 = vpointer; // reset to original value
|
|
return 0;
|
|
|
|
Linsert:
|
|
// Insert at location i
|
|
assert(i < TBLMAX);
|
|
assert(tblmax <= TBLMAX);
|
|
tbl[i] = ci;
|
|
//printf("\tinsert at location %d\n",i);
|
|
|
|
// If it's a scheduled floating point code, we have to adjust
|
|
// the FXCH values
|
|
if (ci->fp_op)
|
|
{ int j;
|
|
|
|
ci->fxch_pre = 0;
|
|
ci->fxch_post = 0; // start over again
|
|
|
|
int fpu = fpustackused;
|
|
for (j = 0; j < tblmax; j++)
|
|
{
|
|
if (tbl[j])
|
|
{
|
|
fpu += tbl[j]->fpuadjust;
|
|
if (fpu >= 8) // if FPU stack overflow
|
|
{ tbl[i] = NULL;
|
|
//printf("fpu stack overflow\n");
|
|
goto Lnoinsert;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (j = tblmax; j > i; j--)
|
|
{
|
|
if (j < TBLMAX && tbl[j])
|
|
conflict(tbl[j],ci,2);
|
|
}
|
|
}
|
|
|
|
if (movesp)
|
|
{ // Adjust [ESP] offsets
|
|
int j;
|
|
|
|
//printf("\tic = %d, inserting at %d\n",ic,i);
|
|
assert((unsigned)tblmax <= TBLMAX);
|
|
for (j = ic + 1; j < i; j++)
|
|
{ Cinfo *cit;
|
|
|
|
cit = tbl[j];
|
|
if (cit)
|
|
{
|
|
c->IEVpointer1 -= cit->spadjust;
|
|
//printf("\t3, spadjust = %d, ptr = x%x\n",cit->spadjust,c->IEVpointer1);
|
|
}
|
|
}
|
|
}
|
|
if (i >= tblmax)
|
|
tblmax = i + 1;
|
|
|
|
// Now do a hack. Look back at immediately preceding instructions,
|
|
// and see if we can swap with a push.
|
|
if (0 && movesp)
|
|
{ int j;
|
|
|
|
while (1)
|
|
{
|
|
for (j = 1; i > j; j++)
|
|
if (tbl[i - j])
|
|
break;
|
|
|
|
if (i >= j && tbl[i - j] &&
|
|
(tbl[i - j]->c->Iop & ~7) == 0x50 && // if PUSH reg1
|
|
(tbl[i - j]->c->Iop & 7) != reg2 && // if reg1 != reg2
|
|
(signed char)c->IEVpointer1 >= REGSIZE)
|
|
{
|
|
//printf("\t-4 prec, i-j=%d, i=%d\n",i-j,i);
|
|
assert((unsigned)i < TBLMAX);
|
|
assert((unsigned)(i - j) < TBLMAX);
|
|
tbl[i] = tbl[i - j];
|
|
tbl[i - j] = ci;
|
|
i -= j;
|
|
c->IEVpointer1 -= REGSIZE;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
//printf("\tinsert\n");
|
|
return 1;
|
|
}
|
|
|
|
|
|
/******************************
|
|
* Insert c into staging area.
|
|
* Returns:
|
|
* 0 could not be scheduled; have to start a new one
|
|
*/
|
|
|
|
int Schedule::stage(code *c)
|
|
{ Cinfo *ci;
|
|
list_t l;
|
|
list_t ln;
|
|
int agi;
|
|
|
|
//printf("stage: "); c->print();
|
|
if (cinfomax == TBLMAX) // if out of space
|
|
goto Lnostage;
|
|
ci = &cinfo[cinfomax++];
|
|
getinfo(ci,c);
|
|
|
|
if (c->Iflags & (CFtarg | CFtarg2 | CFvolatile))
|
|
{
|
|
// Insert anything in stagelist
|
|
for (l = stagelist; l; l = ln)
|
|
{ Cinfo *cs;
|
|
|
|
ln = list_next(l);
|
|
cs = (Cinfo *)list_ptr(l);
|
|
if (!insert(cs))
|
|
return 0;
|
|
list_subtract(&stagelist,cs);
|
|
}
|
|
return insert(ci);
|
|
}
|
|
|
|
// Look through stagelist, and insert any AGI conflicting instructions
|
|
agi = 0;
|
|
for (l = stagelist; l; l = ln)
|
|
{ Cinfo *cs;
|
|
|
|
ln = list_next(l);
|
|
cs = (Cinfo *)list_ptr(l);
|
|
if (pair_agi(cs,ci))
|
|
{
|
|
if (!insert(cs))
|
|
goto Lnostage;
|
|
list_subtract(&stagelist,cs);
|
|
agi = 1; // we put out an AGI
|
|
}
|
|
}
|
|
|
|
// Look through stagelist, and insert any other conflicting instructions
|
|
for (l = stagelist; l; l = ln)
|
|
{ Cinfo *cs;
|
|
|
|
ln = list_next(l);
|
|
cs = (Cinfo *)list_ptr(l);
|
|
if (conflict(cs,ci,0) && // if conflict
|
|
!(cs->flags & ci->flags & CIFLpush))
|
|
{
|
|
if (cs->spadjust)
|
|
{
|
|
// We need to insert all previous adjustments to ESP
|
|
list_t la,lan;
|
|
|
|
for (la = stagelist; la != l; la = lan)
|
|
{ Cinfo *ca;
|
|
|
|
lan = list_next(la);
|
|
ca = (Cinfo *)list_ptr(la);
|
|
if (ca->spadjust)
|
|
{ if (!insert(ca))
|
|
goto Lnostage;
|
|
list_subtract(&stagelist,ca);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!insert(cs))
|
|
goto Lnostage;
|
|
list_subtract(&stagelist,cs);
|
|
}
|
|
}
|
|
|
|
// If floating point opcode, don't stage it, send it right out
|
|
if (!agi && ci->flags & CIFLnostage)
|
|
{
|
|
if (!insert(ci))
|
|
goto Lnostage;
|
|
return 1;
|
|
}
|
|
|
|
list_append(&stagelist,ci); // append to staging list
|
|
return 1;
|
|
|
|
Lnostage:
|
|
return 0;
|
|
}
|
|
|
|
/********************************************
|
|
* Snip off tail of instruction sequence.
|
|
* Returns:
|
|
* next instruction (the tail) or
|
|
* NULL for no more instructions
|
|
*/
|
|
|
|
STATIC code * csnip(code *c)
|
|
{ code **pc;
|
|
unsigned iflags;
|
|
|
|
if (c)
|
|
{ iflags = c->Iflags & CFclassinit;
|
|
while (1)
|
|
{
|
|
pc = &code_next(c);
|
|
c = *pc;
|
|
if (!c)
|
|
break;
|
|
if (c->Iflags & (CFtarg | CFtarg2))
|
|
break;
|
|
if (!(c->Iop == NOP ||
|
|
c->Iop == (ESCAPE | ESClinnum) ||
|
|
c->Iflags & iflags))
|
|
break;
|
|
}
|
|
*pc = NULL;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
|
|
/******************************
|
|
* Schedule Pentium instructions,
|
|
* based on Steve Russell's algorithm.
|
|
*/
|
|
|
|
code *schedule(code *c,regm_t scratch)
|
|
{
|
|
code *cresult = NULL;
|
|
code **pctail = &cresult;
|
|
Schedule sch;
|
|
|
|
sch.initialize(0); // initialize scheduling table
|
|
while (c)
|
|
{
|
|
if ((c->Iop == NOP ||
|
|
((c->Iop & 0xFF) == ESCAPE && c->Iop != (ESCAPE | ESCadjfpu)) ||
|
|
c->Iflags & CFclassinit) &&
|
|
!(c->Iflags & (CFtarg | CFtarg2)))
|
|
{ code *cn;
|
|
|
|
// Just append this instruction to pctail and go to the next one
|
|
*pctail = c;
|
|
cn = code_next(c);
|
|
code_next(c) = NULL;
|
|
pctail = &code_next(c);
|
|
c = cn;
|
|
continue;
|
|
}
|
|
|
|
//printf("init\n");
|
|
sch.initialize(sch.fpustackused); // initialize scheduling table
|
|
|
|
while (c)
|
|
{
|
|
//printf("insert %p\n",c);
|
|
if (!sch.stage(c)) // store c in scheduling table
|
|
break;
|
|
c = csnip(c);
|
|
}
|
|
|
|
//printf("assem %d\n",sch.tblmax);
|
|
pctail = sch.assemble(pctail); // reassemble instruction stream
|
|
}
|
|
|
|
return cresult;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
|
|
/********************************************
|
|
* Replace any occurrence of r1 in EA with r2.
|
|
*/
|
|
|
|
STATIC void repEA(code *c,unsigned r1,unsigned r2)
|
|
{
|
|
unsigned mod,reg,rm;
|
|
unsigned rmn;
|
|
|
|
rmn = c->Irm;
|
|
mod = rmn & 0xC0;
|
|
reg = rmn & modregrm(0,7,0);
|
|
rm = rmn & 7;
|
|
|
|
if (mod == 0xC0 && rm == r1)
|
|
; //c->Irm = mod | reg | r2;
|
|
else if (is32bitaddr(I32,c->Iflags) &&
|
|
// If not disp32
|
|
(rmn & modregrm(3,0,7)) != modregrm(0,0,5))
|
|
{
|
|
if (rm == 4)
|
|
{ // SIB byte addressing
|
|
unsigned sib;
|
|
unsigned base;
|
|
unsigned index;
|
|
|
|
sib = c->Isib;
|
|
base = sib & 7;
|
|
index = (sib >> 3) & 7;
|
|
if (base == r1 &&
|
|
!(r1 == 5 && mod == 0) &&
|
|
!(r2 == 5 && mod == 0)
|
|
)
|
|
base = r2;
|
|
if (index == r1)
|
|
index = r2;
|
|
c->Isib = (sib & 0xC0) | (index << 3) | base;
|
|
}
|
|
else if (rm == r1)
|
|
{
|
|
if (r1 == BP && r2 == SP)
|
|
{ // Replace [EBP] with [ESP]
|
|
c->Irm = mod | reg | 4;
|
|
c->Isib = modregrm(0,4,SP);
|
|
}
|
|
else if (r2 == BP && mod == 0)
|
|
{
|
|
c->Irm = modregrm(1,0,0) | reg | r2;
|
|
c->IFL1 = FLconst;
|
|
c->IEV1.Vint = 0;
|
|
}
|
|
else
|
|
c->Irm = mod | reg | r2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************
|
|
* Instruction scheduler.
|
|
* Input:
|
|
* c list of instructions to schedule
|
|
* scratch scratch registers we can use
|
|
* Returns:
|
|
* revised list of scheduled instructions
|
|
*/
|
|
|
|
/******************************************
|
|
* Swap c1 and c2.
|
|
* c1 comes before c2.
|
|
* Swap in place to not disturb addresses of jmp targets
|
|
*/
|
|
|
|
STATIC void code_swap(code *c1,code *c2)
|
|
{ code cs;
|
|
|
|
// Special case of:
|
|
// PUSH reg1
|
|
// MOV reg2,x[ESP]
|
|
//printf("code_swap(%x, %x)\n",c1,c2);
|
|
if ((c1->Iop & ~7) == 0x50 &&
|
|
c2->Iop == 0x8B &&
|
|
(c2->Irm & modregrm(3,0,7)) == modregrm(1,0,4) &&
|
|
c2->Isib == modregrm(0,4,SP) &&
|
|
c2->IFL1 == FLconst &&
|
|
((signed char)c2->IEVpointer1) >= REGSIZE &&
|
|
(c1->Iop & 7) != ((c2->Irm >> 3) & 7)
|
|
)
|
|
c2->IEVpointer1 -= REGSIZE;
|
|
|
|
|
|
cs = *c2;
|
|
*c2 = *c1;
|
|
*c1 = cs;
|
|
// Retain original CFtarg
|
|
c1->Iflags = (c1->Iflags & ~(CFtarg | CFtarg2)) | (c2->Iflags & (CFtarg | CFtarg2));
|
|
c2->Iflags = (c2->Iflags & ~(CFtarg | CFtarg2)) | (cs.Iflags & (CFtarg | CFtarg2));
|
|
|
|
c1->next = c2->next;
|
|
c2->next = cs.next;
|
|
}
|
|
|
|
code *peephole(code *cstart,regm_t scratch)
|
|
{
|
|
// Look for cases of:
|
|
// MOV r1,r2
|
|
// OP ?,r1
|
|
// we can replace with:
|
|
// MOV r1,r2
|
|
// OP ?,r2
|
|
// to improve pairing
|
|
code *c;
|
|
code *c1;
|
|
unsigned r1,r2;
|
|
unsigned mod,reg,rm;
|
|
|
|
//printf("peephole\n");
|
|
for (c = cstart; c; c = c1)
|
|
{ unsigned char rmi;
|
|
unsigned char rmn;
|
|
|
|
//c->print();
|
|
c1 = cnext(c);
|
|
Ln:
|
|
if (!c1)
|
|
break;
|
|
if (c1->Iflags & (CFtarg | CFtarg2))
|
|
continue;
|
|
|
|
// Do:
|
|
// PUSH reg
|
|
if (I32 && (c->Iop & ~7) == 0x50)
|
|
{ unsigned reg = c->Iop & 7;
|
|
|
|
// MOV [ESP],reg => NOP
|
|
if (c1->Iop == 0x8B &&
|
|
c1->Irm == modregrm(0,reg,4) &&
|
|
c1->Isib == modregrm(0,4,SP))
|
|
{ c1->Iop = NOP;
|
|
continue;
|
|
}
|
|
|
|
// PUSH [ESP] => PUSH reg
|
|
if (c1->Iop == 0xFF &&
|
|
c1->Irm == modregrm(0,6,4) &&
|
|
c1->Isib == modregrm(0,4,SP))
|
|
{ c1->Iop = 0x50 + reg;
|
|
continue;
|
|
}
|
|
|
|
// CMP [ESP],imm => CMP reg,i,,
|
|
if (c1->Iop == 0x83 &&
|
|
c1->Irm == modregrm(0,7,4) &&
|
|
c1->Isib == modregrm(0,4,SP))
|
|
{ c1->Irm = modregrm(3,7,reg);
|
|
if (c1->IFL2 == FLconst && (signed char)c1->IEV2.Vuns == 0)
|
|
{ // to TEST reg,reg
|
|
c1->Iop = (c1->Iop & 1) | 0x84;
|
|
c1->Irm = modregrm(3,reg,reg);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
rmi = c->Irm;
|
|
|
|
// Do:
|
|
// MOV reg,[ESP] => PUSH reg
|
|
// ADD ESP,4 => NOP
|
|
if (I32 && c->Iop == 0x8B && (rmi & 0xC7) == modregrm(0,0,4) &&
|
|
c->Isib == modregrm(0,4,SP) &&
|
|
c1->Iop == 0x83 && (c1->Irm & 0xC7) == modregrm(3,0,SP) &&
|
|
!(c1->Iflags & CFpsw) && c1->IFL2 == FLconst && c1->IEV2.Vint == 4)
|
|
{ unsigned reg = (rmi >> 3) & 7;
|
|
c->Iop = 0x58 + reg;
|
|
c1->Iop = NOP;
|
|
continue;
|
|
}
|
|
|
|
if ((rmi & 0xC0) != 0xC0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Combine two SUBs of the same register
|
|
if (c->Iop == c1->Iop &&
|
|
c->Iop == 0x83 &&
|
|
(rmi & modregrm(3,0,7)) == (c1->Irm & modregrm(3,0,7)) &&
|
|
!(c1->Iflags & CFpsw) &&
|
|
c->IFL2 == FLconst && c1->IFL2 == FLconst
|
|
)
|
|
{ int i = (signed char)c->IEV2.Vint;
|
|
int i1 = (signed char)c1->IEV2.Vint;
|
|
switch ((rmi & modregrm(0,7,0)) | ((c1->Irm & modregrm(0,7,0)) >> 3))
|
|
{
|
|
case (0 << 3) | 0: // ADD, ADD
|
|
case (5 << 3) | 5: // SUB, SUB
|
|
i += i1;
|
|
goto Laa;
|
|
case (0 << 3) | 5: // ADD, SUB
|
|
case (5 << 3) | 0: // SUB, ADD
|
|
i -= i1;
|
|
goto Laa;
|
|
Laa:
|
|
if ((signed char)i != i)
|
|
c->Iop &= ~2;
|
|
c->IEV2.Vint = i;
|
|
c1->Iop = NOP;
|
|
if (i == 0)
|
|
c->Iop = NOP;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (c->Iop == 0x8B) // MOV r1,EA
|
|
{ r1 = (rmi >> 3) & 7;
|
|
r2 = rmi & 7;
|
|
}
|
|
else if (c->Iop == 0x89) // MOV EA,r2
|
|
{ r1 = rmi & 7;
|
|
r2 = (rmi >> 3) & 7;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
rmn = c1->Irm;
|
|
mod = rmn & 0xC0;
|
|
reg = rmn & modregrm(0,7,0);
|
|
rm = rmn & 7;
|
|
if (cod3_EA(c1))
|
|
repEA(c1,r1,r2);
|
|
switch (c1->Iop)
|
|
{
|
|
case 0x50:
|
|
case 0x51:
|
|
case 0x52:
|
|
case 0x53:
|
|
case 0x54:
|
|
case 0x55:
|
|
case 0x56:
|
|
case 0x57: // PUSH reg
|
|
if ((c1->Iop & 7) == r1)
|
|
{ c1->Iop = 0x50 | r2;
|
|
//printf("schedule PUSH reg\n");
|
|
}
|
|
break;
|
|
|
|
case 0x81:
|
|
case 0x83:
|
|
// Look for CMP EA,imm
|
|
if (reg == modregrm(0,7,0))
|
|
{
|
|
if (mod == 0xC0 && rm == r1)
|
|
c1->Irm = mod | reg | r2;
|
|
}
|
|
break;
|
|
|
|
case 0x84: // TEST reg,byte ptr EA
|
|
if (r1 >= 4 || r2 >= 4) // if not a byte register
|
|
break;
|
|
if ((rmn & 0xC0) == 0xC0)
|
|
{
|
|
if ((rmn & 3) == r1)
|
|
{ c1->Irm = rmn = (rmn & modregrm(3,7,4)) | r2;
|
|
//printf("schedule 1\n");
|
|
}
|
|
}
|
|
if ((rmn & modregrm(0,3,0)) == modregrm(0,r1,0))
|
|
{ c1->Irm = (rmn & modregrm(3,4,7)) | modregrm(0,r2,0);
|
|
//printf("schedule 2\n");
|
|
}
|
|
break;
|
|
case 0x85: // TEST reg,word ptr EA
|
|
if ((rmn & 0xC0) == 0xC0)
|
|
{
|
|
if ((rmn & 7) == r1)
|
|
{ c1->Irm = rmn = (rmn & modregrm(3,7,0)) | r2;
|
|
//printf("schedule 3\n");
|
|
}
|
|
}
|
|
if ((rmn & modregrm(0,7,0)) == modregrm(0,r1,0))
|
|
{ c1->Irm = (rmn & modregrm(3,0,7)) | modregrm(0,r2,0);
|
|
//printf("schedule 4\n");
|
|
}
|
|
break;
|
|
|
|
case 0x89: // MOV EA,reg
|
|
if ((rmn & modregrm(0,7,0)) == modregrm(0,r1,0))
|
|
{ c1->Irm = (rmn & modregrm(3,0,7)) | modregrm(0,r2,0);
|
|
//printf("schedule 5\n");
|
|
if (c1->Irm == modregrm(3,r2,r2))
|
|
goto Lnop;
|
|
}
|
|
break;
|
|
|
|
case 0x8B: // MOV reg,EA
|
|
if ((rmn & 0xC0) == 0xC0 &&
|
|
(rmn & 7) == r1) // if EA == r1
|
|
{ c1->Irm = (rmn & modregrm(3,7,0)) | r2;
|
|
//printf("schedule 6\n");
|
|
if (c1->Irm == modregrm(3,r2,r2))
|
|
goto Lnop;
|
|
}
|
|
break;
|
|
|
|
case 0x3C: // CMP AL,imm8
|
|
if (r1 == AX && r2 < 4)
|
|
{ c1->Iop = 0x80;
|
|
c1->Irm = modregrm(3,7,r2);
|
|
//printf("schedule 7, r2 = %d\n", r2);
|
|
}
|
|
break;
|
|
|
|
case 0x3D: // CMP AX,imm16
|
|
if (r1 == AX)
|
|
{ c1->Iop = 0x81;
|
|
c1->Irm = modregrm(3,7,r2);
|
|
if (c1->IFL2 == FLconst &&
|
|
c1->IEV2.Vuns == (signed char)c1->IEV2.Vuns)
|
|
c1->Iop = 0x83;
|
|
//printf("schedule 8\n");
|
|
}
|
|
break;
|
|
}
|
|
continue;
|
|
Lnop:
|
|
c1->Iop = NOP;
|
|
c1 = cnext(c1);
|
|
goto Ln;
|
|
}
|
|
L1: ;
|
|
return cstart;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
|
|
/**********************************************
|
|
* Replace complex instructions with simple ones more conducive
|
|
* to scheduling.
|
|
*/
|
|
|
|
code *simpleops(code *c,regm_t scratch)
|
|
{ code *cstart;
|
|
code **pc;
|
|
unsigned reg;
|
|
code *c2;
|
|
|
|
// Worry about using registers not saved yet by prolog
|
|
scratch &= ~fregsaved;
|
|
|
|
if (!(scratch & (scratch - 1))) // if 0 or 1 registers
|
|
return c;
|
|
|
|
reg = findreg(scratch);
|
|
|
|
cstart = c;
|
|
for (pc = &cstart; *pc; pc = &code_next(*pc))
|
|
{
|
|
c = *pc;
|
|
if (c->Iflags & (CFtarg | CFtarg2 | CFopsize))
|
|
continue;
|
|
if (c->Iop == 0x83 &&
|
|
(c->Irm & modregrm(0,7,0)) == modregrm(0,7,0) &&
|
|
(c->Irm & modregrm(3,0,0)) != modregrm(3,0,0)
|
|
)
|
|
{ // Replace CMP mem,imm with:
|
|
// MOV reg,mem
|
|
// CMP reg,imm
|
|
targ_long imm;
|
|
|
|
//printf("replacing CMP\n");
|
|
c->Iop = 0x8B;
|
|
c->Irm = (c->Irm & modregrm(3,0,7)) | modregrm(0,reg,0);
|
|
|
|
c2 = code_calloc();
|
|
if (reg == AX)
|
|
c2->Iop = 0x3D;
|
|
else
|
|
{ c2->Iop = 0x83;
|
|
c2->Irm = modregrm(3,7,reg);
|
|
}
|
|
c2->IFL2 = c->IFL2;
|
|
c2->IEV2 = c->IEV2;
|
|
|
|
// See if c2 should be replaced by a TEST
|
|
imm = c2->IEV2.Vuns;
|
|
if (!(c2->Iop & 1))
|
|
imm &= 0xFF;
|
|
else if (I32 ? c->Iflags & CFopsize : !(c->Iflags & CFopsize))
|
|
imm = (short) imm;
|
|
if (imm == 0)
|
|
{
|
|
c2->Iop = 0x85; // TEST reg,reg
|
|
c2->Irm = modregrm(3,reg,reg);
|
|
}
|
|
goto L1;
|
|
}
|
|
else if (c->Iop == 0xFF &&
|
|
(c->Irm & modregrm(0,7,0)) == modregrm(0,6,0) &&
|
|
(c->Irm & modregrm(3,0,0)) != modregrm(3,0,0)
|
|
)
|
|
{ // Replace PUSH mem with:
|
|
// MOV reg,mem
|
|
// PUSH reg
|
|
|
|
// printf("replacing PUSH\n");
|
|
c->Iop = 0x8B;
|
|
c->Irm = (c->Irm & modregrm(3,0,7)) | modregrm(0,reg,0);
|
|
|
|
c2 = gen1(NULL,0x50 + reg);
|
|
L1:
|
|
//c->print();
|
|
//c2->print();
|
|
c2->next = c->next;
|
|
c->next = c2;
|
|
|
|
// Switch to another reg
|
|
if (scratch & ~mask[reg])
|
|
reg = findreg(scratch & ~mask[reg]);
|
|
}
|
|
}
|
|
return cstart;
|
|
}
|
|
|
|
#if DEBUG
|
|
static const char *fpops[] = {"fstp","fld","fop"};
|
|
void Cinfo::print()
|
|
{
|
|
Cinfo *ci = this;
|
|
|
|
if (ci == NULL)
|
|
{
|
|
printf("Cinfo 0\n");
|
|
return;
|
|
}
|
|
|
|
printf("Cinfo %p: c %p, pair %x, sz %d, isz %d, flags - ",
|
|
ci,c,pair,sz,isz);
|
|
if (ci->flags & CIFLarraybounds)
|
|
printf("arraybounds,");
|
|
if (ci->flags & CIFLea)
|
|
printf("ea,");
|
|
if (ci->flags & CIFLnostage)
|
|
printf("nostage,");
|
|
if (ci->flags & CIFLpush)
|
|
printf("push,");
|
|
if (ci->flags & ~(CIFLarraybounds|CIFLnostage|CIFLpush|CIFLea))
|
|
printf("bad flag,");
|
|
printf("\n\tr %lx w %lx a %lx reg %x uops %x sibmodrm %x spadjust %ld\n",
|
|
(long)r,(long)w,(long)a,reg,uops,sibmodrm,(long)spadjust);
|
|
if (ci->fp_op)
|
|
printf("\tfp_op %s, fxch_pre %x, fxch_post %x\n",
|
|
fpops[fp_op-1],fxch_pre,fxch_post);
|
|
}
|
|
#endif
|
|
#endif
|