/* * Copyright (C) 2011 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_SRC_COMPILER_COMPILER_IR_H_ #define ART_SRC_COMPILER_COMPILER_IR_H_ #include "codegen/Optimizer.h" #include "CompilerUtility.h" #include #include "oat_compilation_unit.h" namespace art { #define SLOW_FIELD_PATH (cUnit->enableDebug & (1 << kDebugSlowFieldPath)) #define SLOW_INVOKE_PATH (cUnit->enableDebug & (1 << kDebugSlowInvokePath)) #define SLOW_STRING_PATH (cUnit->enableDebug & (1 << kDebugSlowStringPath)) #define SLOW_TYPE_PATH (cUnit->enableDebug & (1 << kDebugSlowTypePath)) #define EXERCISE_SLOWEST_FIELD_PATH (cUnit->enableDebug & \ (1 << kDebugSlowestFieldPath)) #define EXERCISE_SLOWEST_STRING_PATH (cUnit->enableDebug & \ (1 << kDebugSlowestStringPath)) #define EXERCISE_RESOLVE_METHOD (cUnit->enableDebug & \ (1 << kDebugExerciseResolveMethod)) typedef enum RegisterClass { kCoreReg, kFPReg, kAnyReg, } RegisterClass; typedef enum RegLocationType { kLocDalvikFrame = 0, // Normal Dalvik register kLocPhysReg, kLocSpill, } RegLocationType; typedef struct PromotionMap { RegLocationType coreLocation:3; u1 coreReg; RegLocationType fpLocation:3; u1 fpReg; bool firstInPair; } PromotionMap; typedef struct RegLocation { RegLocationType location:3; unsigned wide:1; unsigned defined:1; // Do we know the type? unsigned fp:1; // Floating point? unsigned core:1; // Non-floating point? unsigned highWord:1; // High word of pair? unsigned home:1; // Does this represent the home location? u1 lowReg; // First physical register u1 highReg; // 2nd physical register (if wide) s2 sRegLow; // SSA name for low Dalvik word } RegLocation; /* * Data structure tracking the mapping between a Dalvik register (pair) and a * native register (pair). The idea is to reuse the previously loaded value * if possible, otherwise to keep the value in a native register as long as * possible. */ typedef struct RegisterInfo { int reg; // Reg number bool inUse; // Has it been allocated? bool isTemp; // Can allocate as temp? bool pair; // Part of a register pair? int partner; // If pair, other reg of pair bool live; // Is there an associated SSA name? bool dirty; // If live, is it dirty? int sReg; // Name of live value struct LIR *defStart; // Starting inst in last def sequence struct LIR *defEnd; // Ending inst in last def sequence } RegisterInfo; typedef struct RegisterPool { int numCoreRegs; RegisterInfo *coreRegs; int nextCoreReg; int numFPRegs; RegisterInfo *FPRegs; int nextFPReg; } RegisterPool; #define INVALID_SREG (-1) #define INVALID_VREG (0xFFFFU) #define INVALID_REG (0xFF) #define INVALID_OFFSET (-1) /* * Some code patterns cause the generation of excessively large * methods - in particular initialization sequences. There isn't much * benefit in optimizing these methods, and the cost can be very high. * We attempt to identify these cases, and avoid performing most dataflow * analysis. Two thresholds are used - one for known initializers and one * for everything else. */ #define MANY_BLOCKS_INITIALIZER 1000 /* Threshold for switching dataflow off */ #define MANY_BLOCKS 4000 /* Non-initializer threshold */ typedef enum BBType { kEntryBlock, kDalvikByteCode, kExitBlock, kExceptionHandling, kCatchEntry, } BBType; /* Utility macros to traverse the LIR list */ #define NEXT_LIR(lir) (lir->next) #define PREV_LIR(lir) (lir->prev) #define NEXT_LIR_LVALUE(lir) (lir)->next #define PREV_LIR_LVALUE(lir) (lir)->prev typedef struct LIR { int offset; // Offset of this instruction int dalvikOffset; // Offset of Dalvik opcode struct LIR* next; struct LIR* prev; struct LIR* target; int opcode; int operands[4]; // [0..3] = [dest, src1, src2, extra] struct { bool isNop:1; // LIR is optimized away bool pcRelFixup:1; // May need pc-relative fixup unsigned int age:4; // default is 0, set lazily by the optimizer unsigned int size:4; // in bytes unsigned int unused:22; } flags; int aliasInfo; // For Dalvik register & litpool disambiguation u8 useMask; // Resource mask for use u8 defMask; // Resource mask for def } LIR; enum ExtendedMIROpcode { kMirOpFirst = kNumPackedOpcodes, kMirOpPhi = kMirOpFirst, kMirOpNullNRangeUpCheck, kMirOpNullNRangeDownCheck, kMirOpLowerBound, kMirOpPunt, kMirOpCheckInlinePrediction, // Gen checks for predicted inlining kMirOpLast, }; struct SSARepresentation; typedef enum { kMIRIgnoreNullCheck = 0, kMIRNullCheckOnly, kMIRIgnoreRangeCheck, kMIRRangeCheckOnly, kMIRInlined, // Invoke is inlined (ie dead) kMIRInlinedPred, // Invoke is inlined via prediction kMIRCallee, // Instruction is inlined from callee kMIRIgnoreSuspendCheck, } MIROptimizationFlagPositons; #define MIR_IGNORE_NULL_CHECK (1 << kMIRIgnoreNullCheck) #define MIR_NULL_CHECK_ONLY (1 << kMIRNullCheckOnly) #define MIR_IGNORE_RANGE_CHECK (1 << kMIRIgnoreRangeCheck) #define MIR_RANGE_CHECK_ONLY (1 << kMIRRangeCheckOnly) #define MIR_INLINED (1 << kMIRInlined) #define MIR_INLINED_PRED (1 << kMIRInlinedPred) #define MIR_CALLEE (1 << kMIRCallee) #define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck) typedef struct CallsiteInfo { const char* classDescriptor; Object* classLoader; const Method* method; LIR* misPredBranchOver; } CallsiteInfo; typedef struct MIR { DecodedInstruction dalvikInsn; unsigned int width; unsigned int offset; struct MIR* prev; struct MIR* next; struct SSARepresentation* ssaRep; int optimizationFlags; int seqNum; union { // Used by the inlined insn from the callee to find the mother method const Method* calleeMethod; // Used by the inlined invoke to find the class and method pointers CallsiteInfo* callsiteInfo; // Used to quickly locate all Phi opcodes struct MIR* phiNext; } meta; } MIR; struct BasicBlockDataFlow; /* For successorBlockList */ typedef enum BlockListType { kNotUsed = 0, kCatch, kPackedSwitch, kSparseSwitch, } BlockListType; typedef struct BasicBlock { int id; int dfsId; bool visited; bool hidden; bool catchEntry; unsigned int startOffset; const Method* containingMethod; // For blocks from the callee BBType blockType; bool needFallThroughBranch; // For blocks ended due to length limit bool isFallThroughFromInvoke; // True means the block needs alignment MIR* firstMIRInsn; MIR* lastMIRInsn; struct BasicBlock* fallThrough; struct BasicBlock* taken; struct BasicBlock* iDom; // Immediate dominator struct BasicBlockDataFlow* dataFlowInfo; GrowableList* predecessors; ArenaBitVector* dominators; ArenaBitVector* iDominated; // Set nodes being immediately dominated ArenaBitVector* domFrontier; // Dominance frontier struct { // For one-to-many successors like BlockListType blockListType; // switch and exception handling GrowableList blocks; } successorBlockList; } BasicBlock; /* * The "blocks" field in "successorBlockList" points to an array of * elements with the type "SuccessorBlockInfo". * For catch blocks, key is type index for the exception. * For swtich blocks, key is the case value. */ typedef struct SuccessorBlockInfo { BasicBlock* block; int key; } SuccessorBlockInfo; struct LoopAnalysis; struct RegisterPool; struct ArenaMemBlock; struct Memstats; typedef enum AssemblerStatus { kSuccess, kRetryAll, kRetryHalve } AssemblerStatus; #define NOTVISITED (-1) typedef struct CompilationUnit { int numInsts; int numBlocks; GrowableList blockList; Compiler* compiler; // Compiler driving this compiler ClassLinker* class_linker; // Linker to resolve fields and methods const DexFile* dex_file; // DexFile containing the method being compiled DexCache* dex_cache; // DexFile's corresponding cache const ClassLoader* class_loader; // compiling method's class loader uint32_t method_idx; // compiling method's index into method_ids of DexFile const DexFile::CodeItem* code_item; // compiling method's DexFile code_item uint32_t access_flags; // compiling method's access flags const char* shorty; // compiling method's shorty LIR* firstLIRInsn; LIR* lastLIRInsn; LIR* literalList; // Constants LIR* classPointerList; // Relocatable int numClassPointers; LIR* chainCellOffsetLIR; uint32_t disableOpt; // optControlVector flags uint32_t enableDebug; // debugControlVector flags int headerSize; // bytes before the first code ptr int dataOffset; // starting offset of literal pool int totalSize; // header + code size AssemblerStatus assemblerStatus; // Success or fix and retry int assemblerRetries; std::vector codeBuffer; std::vector mappingTable; std::vector coreVmapTable; std::vector fpVmapTable; bool genDebugger; // Generate code for debugger bool printMe; bool hasClassLiterals; // Contains class ptrs used as literals bool hasLoop; // Contains a loop bool hasInvoke; // Contains an invoke instruction bool heapMemOp; // Mark mem ops for self verification bool usesLinkRegister; // For self-verification only bool methodTraceSupport; // For TraceView profiling struct RegisterPool* regPool; int optRound; // round number to tell an LIR's age OatInstructionSetType instructionSet; /* Number of total regs used in the whole cUnit after SSA transformation */ int numSSARegs; /* Map SSA reg i to the Dalvik[15..0]/Sub[31..16] pair. */ GrowableList* ssaToDalvikMap; /* The following are new data structures to support SSA representations */ /* Map original Dalvik reg i to the SSA[15..0]/Sub[31..16] pair */ int* dalvikToSSAMap; // length == method->registersSize int* SSALastDefs; // length == method->registersSize ArenaBitVector* isConstantV; // length == numSSAReg int* constantValues; // length == numSSAReg int* phiAliasMap; // length == numSSAReg MIR* phiList; /* Map SSA names to location */ RegLocation* regLocation; int sequenceNumber; /* Keep track of Dalvik vReg to physical register mappings */ PromotionMap* promotionMap; /* * Set to the Dalvik PC of the switch instruction if it has more than * MAX_CHAINED_SWITCH_CASES cases. */ const u2* switchOverflowPad; int numReachableBlocks; int numDalvikRegisters; // method->registersSize + inlined BasicBlock* entryBlock; BasicBlock* exitBlock; BasicBlock* curBlock; BasicBlock* nextCodegenBlock; // for extended trace codegen GrowableList dfsOrder; GrowableList dfsPostOrder; GrowableList domPostOrderTraversal; GrowableList throwLaunchpads; GrowableList suspendLaunchpads; int* iDomList; ArenaBitVector* tryBlockAddr; ArenaBitVector** defBlockMatrix; // numDalvikRegister x numBlocks ArenaBitVector* tempBlockV; ArenaBitVector* tempDalvikRegisterV; ArenaBitVector* tempSSARegisterV; // numSSARegs bool printSSANames; void* blockLabelList; bool quitLoopMode; // cold path/complex bytecode int preservedRegsUsed; // How many callee save regs used /* * Frame layout details. * NOTE: for debug support it will be necessary to add a structure * to map the Dalvik virtual registers to the promoted registers. * NOTE: "num" fields are in 4-byte words, "Size" and "Offset" in bytes. */ int numIns; int numOuts; int numRegs; // Unlike numDalvikRegisters, does not include ins int numCoreSpills; int numFPSpills; int numPadding; // # of 4-byte padding cells int regsOffset; // sp-relative offset to beginning of Dalvik regs int insOffset; // sp-relative offset to beginning of Dalvik ins int frameSize; unsigned int coreSpillMask; unsigned int fpSpillMask; unsigned int attrs; /* * CLEANUP/RESTRUCTURE: The code generation utilities don't have a built-in * mechanism to propagate the original Dalvik opcode address to the * associated generated instructions. For the trace compiler, this wasn't * necessary because the interpreter handled all throws and debugging * requests. For now we'll handle this by placing the Dalvik offset * in the CompilationUnit struct before codegen for each instruction. * The low-level LIR creation utilites will pull it from here. Should * be rewritten. */ int currentDalvikOffset; GrowableList switchTables; GrowableList fillArrayData; const u2* insns; u4 insnsSize; bool disableDataflow; // Skip dataflow analysis if possible std::map blockMap; // findBlock lookup cache std::map boundaryMap; // boundary lookup cache int defCount; // Used to estimate number of SSA names std::string* compilerMethodMatch; bool compilerFlipMatch; struct ArenaMemBlock* arenaHead; struct ArenaMemBlock* currentArena; int numArenaBlocks; struct Memstats* mstats; } CompilationUnit; typedef enum OpSize { kWord, kLong, kSingle, kDouble, kUnsignedHalf, kSignedHalf, kUnsignedByte, kSignedByte, } OpSize; typedef enum OpKind { kOpMov, kOpMvn, kOpCmp, kOpLsl, kOpLsr, kOpAsr, kOpRor, kOpNot, kOpAnd, kOpOr, kOpXor, kOpNeg, kOpAdd, kOpAdc, kOpSub, kOpSbc, kOpRsub, kOpMul, kOpDiv, kOpRem, kOpBic, kOpCmn, kOpTst, kOpBkpt, kOpBlx, kOpPush, kOpPop, kOp2Char, kOp2Short, kOp2Byte, kOpCondBr, kOpUncondBr, kOpBx, kOpInvalid, } OpKind; typedef enum ConditionCode { kCondEq, kCondNe, kCondCs, kCondCc, kCondMi, kCondPl, kCondVs, kCondVc, kCondHi, kCondLs, kCondGe, kCondLt, kCondGt, kCondLe, kCondAl, kCondNv, } ConditionCode; typedef enum ThrowKind { kThrowNullPointer, kThrowDivZero, kThrowArrayBounds, kThrowVerificationError, kThrowNegArraySize, kThrowNoSuchMethod, kThrowStackOverflow, } ThrowKind; typedef struct SwitchTable { int offset; const u2* table; // Original dex table int vaddr; // Dalvik offset of switch opcode LIR* bxInst; // Switch indirect branch instruction LIR** targets; // Array of case targets } SwitchTable; typedef struct FillArrayData { int offset; const u2* table; // Original dex table int size; int vaddr; // Dalvik offset of OP_FILL_ARRAY_DATA opcode } FillArrayData; BasicBlock* oatNewBB(CompilationUnit* cUnit, BBType blockType, int blockId); void oatAppendMIR(BasicBlock* bb, MIR* mir); void oatPrependMIR(BasicBlock* bb, MIR* mir); void oatInsertMIRAfter(BasicBlock* bb, MIR* currentMIR, MIR* newMIR); void oatAppendLIR(CompilationUnit* cUnit, LIR* lir); void oatInsertLIRBefore(LIR* currentLIR, LIR* newLIR); void oatInsertLIRAfter(LIR* currentLIR, LIR* newLIR); /* Debug Utilities */ void oatDumpCompilationUnit(CompilationUnit* cUnit); } // namespace art #endif // ART_SRC_COMPILER_COMPILER_IR_H_