blob: 32bb1f8cef9e84f9319e3098d6cfbe458a7038e3 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_COMPILER_COMPILER_CODEGEN_X86_X86LIR_H_
#define ART_COMPILER_COMPILER_CODEGEN_X86_X86LIR_H_
#include "../../Dalvik.h"
#include "../../CompilerInternals.h"
namespace art {
// Set to 1 to measure cost of suspend check
#define NO_SUSPEND 0
/*
* Runtime register conventions. We consider both x86, x86-64 and x32 (32bit mode x86-64), although
* we currently only target x86. The ABI has different conventions and we hope to have a single
* convention to simplify code generation. Changing something that is callee save and making it
* caller save places a burden on up-calls to save/restore the callee save register, however, there
* are few registers that are callee save in the ABI. Changing something that is caller save and
* making it callee save places a burden on down-calls to save/restore the callee save register.
* For these reasons we aim to match native conventions for caller and callee save
*
* General Purpose Register:
* Native: x86 | x86-64 / x32 | ART
* r0/eax: caller save | caller save | caller, Method*, scratch, return value
* r1/ecx: caller save | caller save, arg4 | caller, arg2, scratch
* r2/edx: caller save | caller save, arg3 | caller, arg1, scratch, high half of long return
* r3/ebx: callee save | callee save | callee, available for dalvik register promotion
* r4/esp: stack pointer
* r5/ebp: callee save | callee save | callee, available for dalvik register promotion
* r6/esi: callEE save | callER save, arg2 | callee, available for dalvik register promotion
* r7/edi: callEE save | callER save, arg1 | callee, available for dalvik register promotion
* --- x86-64/x32 registers
* Native: x86-64 / x32 | ART
* r8: caller save, arg5 | caller, scratch
* r9: caller save, arg6 | caller, scratch
* r10: caller save | caller, scratch
* r11: caller save | caller, scratch
* r12: callee save | callee, available for dalvik register promotion
* r13: callee save | callee, available for dalvik register promotion
* r14: callee save | callee, available for dalvik register promotion
* r15: callee save | callee, available for dalvik register promotion
*
* There is no rSELF, instead on x86 fs: has a base address of Thread::Current, whereas on
* x86-64/x32 gs: holds it.
*
* For floating point we don't support CPUs without SSE2 support (ie newer than PIII):
* Native: x86 | x86-64 / x32 | ART
* XMM0: caller save |caller save, arg1 | caller, float/double return value (except for native x86 code)
* XMM1: caller save |caller save, arg2 | caller, scratch
* XMM2: caller save |caller save, arg3 | caller, scratch
* XMM3: caller save |caller save, arg4 | caller, scratch
* XMM4: caller save |caller save, arg5 | caller, scratch
* XMM5: caller save |caller save, arg6 | caller, scratch
* XMM6: caller save |caller save, arg7 | caller, scratch
* XMM7: caller save |caller save, arg8 | caller, scratch
* --- x86-64/x32 registers
* XMM8 .. 15: caller save
*
* X87 is a necessary evil outside of ART code:
* ST0: x86 float/double native return value, caller save
* ST1 .. ST7: caller save
*
* Stack frame diagram (stack grows down, higher addresses at top):
*
* +------------------------+
* | IN[ins-1] | {Note: resides in caller's frame}
* | . |
* | IN[0] |
* | caller's Method* |
* +========================+ {Note: start of callee's frame}
* | return address | {pushed by call}
* | spill region | {variable sized}
* +------------------------+
* | ...filler word... | {Note: used as 2nd word of V[locals-1] if long]
* +------------------------+
* | V[locals-1] |
* | V[locals-2] |
* | . |
* | . |
* | V[1] |
* | V[0] |
* +------------------------+
* | 0 to 3 words padding |
* +------------------------+
* | OUT[outs-1] |
* | OUT[outs-2] |
* | . |
* | OUT[0] |
* | curMethod* | <<== sp w/ 16-byte alignment
* +========================+
*/
/* Offset to distingish FP regs */
#define FP_REG_OFFSET 16
/* Offset to distinguish DP FP regs */
#define FP_DOUBLE 32
/* Offset to distingish the extra regs */
#define EXTRA_REG_OFFSET 64
/* Reg types */
#define REGTYPE(x) (x & (FP_REG_OFFSET | FP_DOUBLE))
#define FPREG(x) ((x & FP_REG_OFFSET) == FP_REG_OFFSET)
#define EXTRAREG(x) ((x & EXTRA_REG_OFFSET) == EXTRA_REG_OFFSET)
#define LOWREG(x) ((x & 0x1f) == x)
#define DOUBLEREG(x) ((x & FP_DOUBLE) == FP_DOUBLE)
#define SINGLEREG(x) (FPREG(x) && !DOUBLEREG(x))
/*
* Note: the low register of a floating point pair is sufficient to
* create the name of a double, but require both names to be passed to
* allow for asserts to verify that the pair is consecutive if significant
* rework is done in this area. Also, it is a good reminder in the calling
* code that reg locations always describe doubles as a pair of singles.
*/
#define S2D(x,y) ((x) | FP_DOUBLE)
/* Mask to strip off fp flags */
#define FP_REG_MASK (FP_REG_OFFSET-1)
/* non-existent Dalvik register */
#define vNone (-1)
/* non-existant physical register */
#define rNone (-1)
/* RegisterLocation templates return values (r0, or r0/r1) */
#define LOC_C_RETURN {kLocPhysReg, 0, 0, 0, 0, 0, 1, rAX, INVALID_REG,\
INVALID_SREG}
#define LOC_C_RETURN_WIDE {kLocPhysReg, 1, 0, 0, 0, 0, 1, rAX, rDX, INVALID_SREG}
typedef enum ResourceEncodingPos {
kGPReg0 = 0,
kRegSP = 4,
kRegLR = -1,
kFPReg0 = 16, // xmm0 .. xmm7/xmm15
kFPRegEnd = 32,
kRegEnd = kFPRegEnd,
kCCode = kRegEnd,
// The following four bits are for memory disambiguation
kDalvikReg, // 1 Dalvik Frame (can be fully disambiguated)
kLiteral, // 2 Literal pool (can be fully disambiguated)
kHeapRef, // 3 Somewhere on the heap (alias with any other heap)
kMustNotAlias, // 4 Guaranteed to be non-alias (eg *(r6+x))
} ResourceEncodingPos;
#define ENCODE_REG_LIST(N) ((u8) N)
#define ENCODE_REG_SP (1ULL << kRegSP)
#define ENCODE_CCODE (1ULL << kCCode)
#define ENCODE_FP_STATUS (1ULL << kFPStatus)
/* Abstract memory locations */
#define ENCODE_DALVIK_REG (1ULL << kDalvikReg)
#define ENCODE_LITERAL (1ULL << kLiteral)
#define ENCODE_HEAP_REF (1ULL << kHeapRef)
#define ENCODE_MUST_NOT_ALIAS (1ULL << kMustNotAlias)
#define ENCODE_ALL (~0ULL)
#define ENCODE_MEM (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
#define DECODE_ALIAS_INFO_REG(X) (X & 0xffff)
#define DECODE_ALIAS_INFO_WIDE(X) ((X & 0x80000000) ? 1 : 0)
/*
* Annotate special-purpose core registers:
*/
typedef enum NativeRegisterPool {
r0 = 0,
rAX = r0,
r1 = 1,
rCX = r1,
r2 = 2,
rDX = r2,
r3 = 3,
rBX = r3,
r4sp = 4,
rSP =r4sp,
r5 = 5,
rBP = r5,
r6 = 6,
rSI = r6,
r7 = 7,
rDI = r7,
r8 = 8,
r9 = 9,
r10 = 10,
r11 = 11,
r12 = 12,
r13 = 13,
r14 = 14,
r15 = 15,
fr0 = 0 + FP_REG_OFFSET,
fr1 = 1 + FP_REG_OFFSET,
fr2 = 2 + FP_REG_OFFSET,
fr3 = 3 + FP_REG_OFFSET,
fr4 = 4 + FP_REG_OFFSET,
fr5 = 5 + FP_REG_OFFSET,
fr6 = 6 + FP_REG_OFFSET,
fr7 = 7 + FP_REG_OFFSET,
fr8 = 8 + FP_REG_OFFSET,
fr9 = 9 + FP_REG_OFFSET,
fr10 = 10 + FP_REG_OFFSET,
fr11 = 11 + FP_REG_OFFSET,
fr12 = 12 + FP_REG_OFFSET,
fr13 = 13 + FP_REG_OFFSET,
fr14 = 14 + FP_REG_OFFSET,
fr15 = 15 + FP_REG_OFFSET,
} NativeRegisterPool;
/*
* Target-independent aliases
*/
#define rARG0 rAX
#define rARG1 rDX
#define rARG2 rCX
#define rRET0 rAX
#define rRET1 rDX
#define isPseudoOpcode(opCode) ((int)(opCode) < 0)
/*
* The following enum defines the list of supported X86 instructions by the
* assembler. Their corresponding EncodingMap positions will be defined in
* Assemble.cc.
*/
typedef enum X86OpCode {
kPseudoSuspendTarget = -15,
kPseudoThrowTarget = -14,
kPseudoCaseLabel = -13,
kPseudoMethodEntry = -12,
kPseudoMethodExit = -11,
kPseudoBarrier = -10,
kPseudoExtended = -9,
kPseudoSSARep = -8,
kPseudoEntryBlock = -7,
kPseudoExitBlock = -6,
kPseudoTargetLabel = -5,
kPseudoDalvikByteCodeBoundary = -4,
kPseudoPseudoAlign4 = -3,
kPseudoEHBlockLabel = -2,
kPseudoNormalBlockLabel = -1,
kX86First,
kX8632BitData = kX86First, /* data [31..0] */
// Define groups of binary operations
// RI - Register Immediate - opcode reg, #immediate
// - lir operands - 0: reg, 1: immediate
// MI - Memory Immediate - opcode [base + disp], #immediate
// - lir operands - 0: base, 1: disp, 2: immediate
// AI - Array Immediate - opcode [base + index * scale + disp], #immediate
// - lir operands - 0: base, 1: index, 2: scale, 3: disp 4: immediate
// RR - Register Register - opcode reg1, reg2
// - lir operands - 0: reg1, 1: reg2
// RM - Register Memory - opcode reg, [base + disp]
// - lir operands - 0: reg, 1: base, 2: disp
// RA - Register Array - opcode reg, [base + index * scale + disp]
// - lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: disp
// MR - Memory Register - opcode [base + disp], reg
// - lir operands - 0: base, 1: disp, 2: reg
// AR - Array Register - opcode [base + index * scale + disp], reg
// - lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
#define BinaryOpCode(opcode) \
opcode ## RI, opcode ## MI, opcode ##AI, \
opcode ## RR, opcode ## RM, opcode ##RA, \
opcode ## MR, opcode ## AR
BinaryOpCode(kOpAdd),
BinaryOpCode(kOpOr),
BinaryOpCode(kOpAdc),
BinaryOpCode(kOpSbb),
BinaryOpCode(kOpAnd),
BinaryOpCode(kOpSub),
BinaryOpCode(kOpXor),
BinaryOpCode(kOpCmp),
BinaryOpCode(kOpMov),
#undef BinaryOpCode
kX86Last
} X86OpCode;
/* Instruction assembly fieldLoc kind */
typedef enum X86EncodingKind {
kData, // Special case for raw data
kRegImm, kMemImm, kArrayImm, // RI, MI, AI instruction kinds
kRegReg, kRegMem, kRegArray, // RR, RM, RA instruction kinds
kMemReg, kArrayReg, // MR and AR instruction kinds
kUnimplemented // Encoding used when an instruction isn't yet implemented.
} X86EncodingKind;
/* A form of instruction with an opcode byte and a secondary opcode within the modrm byte */
typedef struct OpcodeModRMOpcode {
uint8_t opcode; // 1 byte opcode
uint8_t modrm_opcode; // 3 bit opcode that gets encoded in the register bits of the modrm byte
} OpcodeModRMOpcode;
/* Struct used to define the EncodingMap positions for each X86 opcode */
typedef struct X86EncodingMap {
X86OpCode opcode; // e.g. kOpAddRI
X86EncodingKind kind; // Used to discriminate in the union below
int flags;
union {
struct {
uint8_t rax8_i8_opcode;
uint8_t rax32_i32_opcode;
OpcodeModRMOpcode rm8_i8_opcode;
OpcodeModRMOpcode rm32_i32_opcode;
OpcodeModRMOpcode rm32_i8_opcode;
} RegMem_Immediate; // kind: kRegImm, kMemImm, kArrayImm
struct {
uint8_t r8_rm8_opcode;
uint8_t r32_rm32_opcode;
} Reg_RegMem; // kind: kRegReg, kRegMem, kRegArray
struct {
uint8_t rm8_r8_opcode;
uint8_t rm32_r32_opcode;
} RegMem_Reg; // kind: kMemReg, kArrayReg
// This is a convenience for static initialization where the kind doesn't require opcode data.
int unused; // kind: kData, kUnimplemented
} skeleton;
const char *name;
const char* fmt;
} X86EncodingMap;
extern X86EncodingMap EncodingMap[kX86Last];
// FIXME: mem barrier type - what do we do for x86?
#define kSY 0
#define kST 0
/* Bit flags describing the behavior of each native opcode */
typedef enum X86OpFeatureFlags {
kIsBranch = 0,
kRegDef0,
kRegDef1,
kRegDefSP,
kRegDefList0,
kRegDefList1,
kRegUse0,
kRegUse1,
kRegUse2,
kRegUse3,
kRegUseSP,
kRegUseList0,
kRegUseList1,
kNoOperand,
kIsUnaryOp,
kIsBinaryOp,
kIsTertiaryOp,
kIsQuadOp,
kIsIT,
kSetsCCodes,
kUsesCCodes,
kMemLoad,
kMemStore,
kPCRelFixup,
// FIXME: add NEEDS_FIXUP to instruction attributes
} X86OpFeatureFlags;
#define IS_LOAD (1 << kMemLoad)
#define IS_STORE (1 << kMemStore)
#define IS_BRANCH (1 << kIsBranch)
#define REG_DEF0 (1 << kRegDef0)
#define REG_DEF1 (1 << kRegDef1)
#define REG_DEF_SP (1 << kRegDefSP)
#define REG_DEF_LR (1 << kRegDefLR)
#define REG_DEF_LIST0 (1 << kRegDefList0)
#define REG_DEF_LIST1 (1 << kRegDefList1)
#define REG_USE0 (1 << kRegUse0)
#define REG_USE1 (1 << kRegUse1)
#define REG_USE2 (1 << kRegUse2)
#define REG_USE3 (1 << kRegUse3)
#define REG_USE_SP (1 << kRegUseSP)
#define REG_USE_PC (1 << kRegUsePC)
#define REG_USE_LIST0 (1 << kRegUseList0)
#define REG_USE_LIST1 (1 << kRegUseList1)
#define NO_OPERAND (1 << kNoOperand)
#define IS_UNARY_OP (1 << kIsUnaryOp)
#define IS_BINARY_OP (1 << kIsBinaryOp)
#define IS_TERTIARY_OP (1 << kIsTertiaryOp)
#define IS_QUAD_OP (1 << kIsQuadOp)
#define IS_IT (1 << kIsIT)
#define SETS_CCODES (1 << kSetsCCodes)
#define USES_CCODES (1 << kUsesCCodes)
#define NEEDS_FIXUP (1 << kPCRelFixup)
/* attributes, included for compatibility */
#define REG_DEF_FPCS_LIST0 (0)
#define REG_DEF_FPCS_LIST2 (0)
/* Common combo register usage patterns */
#define REG_USE01 (REG_USE0 | REG_USE1)
#define REG_USE02 (REG_USE0 | REG_USE2)
#define REG_USE012 (REG_USE01 | REG_USE2)
#define REG_USE12 (REG_USE1 | REG_USE2)
#define REG_USE23 (REG_USE2 | REG_USE3)
#define REG_DEF01 (REG_DEF0 | REG_DEF1)
#define REG_DEF0_USE0 (REG_DEF0 | REG_USE0)
#define REG_DEF0_USE1 (REG_DEF0 | REG_USE1)
#define REG_DEF0_USE2 (REG_DEF0 | REG_USE2)
#define REG_DEF0_USE01 (REG_DEF0 | REG_USE01)
#define REG_DEF0_USE12 (REG_DEF0 | REG_USE12)
#define REG_DEF01_USE2 (REG_DEF0 | REG_DEF1 | REG_USE2)
/* Keys for target-specific scheduling and other optimization hints */
typedef enum X86TargetOptHints {
kMaxHoistDistance,
} X86TargetOptHints;
#define IS_SIMM8(v) ((-128 <= (v)) && ((v) <= 127))
} // namespace art
#endif // ART_COMPILER_COMPILER_CODEGEN_X86_X86LIR_H_