diff options
Diffstat (limited to 'vm/compiler')
35 files changed, 6957 insertions, 0 deletions
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c new file mode 100644 index 000000000..dc2497793 --- /dev/null +++ b/vm/compiler/Compiler.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2009 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. + */ + +#include <sys/mman.h> +#include <errno.h> + +#include "Dalvik.h" +#include "interp/Jit.h" +#include "CompilerInternals.h" + + +static inline bool workQueueLength(void) +{ + return gDvmJit.compilerQueueLength; +} + +static CompilerWorkOrder workDequeue(void) +{ + assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind + != kWorkOrderInvalid); + CompilerWorkOrder work = + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex]; + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind = + kWorkOrderInvalid; + if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) { + gDvmJit.compilerWorkDequeueIndex = 0; + } + gDvmJit.compilerQueueLength--; + + /* Remember the high water mark of the queue length */ + if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued) + gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength; + + return work; +} + +bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info) +{ + int cc; + int i; + int numWork; + + dvmLockMutex(&gDvmJit.compilerLock); + + /* Queue full */ + if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE || + gDvmJit.codeCacheFull == true) { + dvmUnlockMutex(&gDvmJit.compilerLock); + return false; + } + + for (numWork = gDvmJit.compilerQueueLength, + i = gDvmJit.compilerWorkDequeueIndex; + numWork > 0; + numWork--) { + /* Already enqueued */ + if (gDvmJit.compilerWorkQueue[i++].pc == pc) + goto done; + /* Wrap around */ + if (i == COMPILER_WORK_QUEUE_SIZE) + i = 0; + } + + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].pc = pc; + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].kind = kind; + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].info = info; + gDvmJit.compilerWorkEnqueueIndex++; + if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE) + gDvmJit.compilerWorkEnqueueIndex = 0; + gDvmJit.compilerQueueLength++; + cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity); + assert(cc == 0); + +done: + dvmUnlockMutex(&gDvmJit.compilerLock); + return true; +} + +/* Block until queue length is 0 */ +void dvmCompilerDrainQueue(void) +{ + dvmLockMutex(&gDvmJit.compilerLock); + while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread) { + pthread_cond_wait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock); + } + dvmUnlockMutex(&gDvmJit.compilerLock); +} + +static void *compilerThreadStart(void *arg) +{ + dvmLockMutex(&gDvmJit.compilerLock); + /* + * Since the compiler thread will not touch any objects on the heap once + * being created, we just fake its state as VMWAIT so that it can be a + * bit late when there is suspend request pending. + */ + dvmChangeStatus(NULL, THREAD_VMWAIT); + while (!gDvmJit.haltCompilerThread) { + if (workQueueLength() == 0) { + int cc; + cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty); + assert(cc == 0); + pthread_cond_wait(&gDvmJit.compilerQueueActivity, + &gDvmJit.compilerLock); + continue; + } else { + do { + void *compiledCodePtr; + CompilerWorkOrder work = workDequeue(); + dvmUnlockMutex(&gDvmJit.compilerLock); + /* Check whether there is a suspend request on me */ + dvmCheckSuspendPending(NULL); + if (gDvmJit.haltCompilerThread) { + LOGD("Compiler shutdown in progress - discarding request"); + } else { + compiledCodePtr = dvmCompilerDoWork(&work); + /* Compilation is successful */ + if (compiledCodePtr) { + dvmJitSetCodeAddr(work.pc, compiledCodePtr); + } + } + free(work.info); + dvmLockMutex(&gDvmJit.compilerLock); + } while (workQueueLength() != 0); + } + } + pthread_cond_signal(&gDvmJit.compilerQueueEmpty); + dvmUnlockMutex(&gDvmJit.compilerLock); + return NULL; +} + +bool dvmCompilerSetupCodeCache(void) +{ + extern void dvmCompilerTemplateStart(void); + extern void dmvCompilerTemplateEnd(void); + + /* Allocate the code cache */ + gDvmJit.codeCache = mmap(0, CODE_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (gDvmJit.codeCache == MAP_FAILED) { + LOGE("Failed to create the code cache: %s\n", strerror(errno)); + return false; + } + + /* Copy the template code into the beginning of the code cache */ + int templateSize = (intptr_t) dmvCompilerTemplateEnd - + (intptr_t) dvmCompilerTemplateStart; + memcpy((void *) gDvmJit.codeCache, + (void *) dvmCompilerTemplateStart, + templateSize); + gDvmJit.codeCacheByteUsed = templateSize; + + /* Flush dcache and invalidate the icache to maintain coherence */ + cacheflush((intptr_t) gDvmJit.codeCache, + (intptr_t) gDvmJit.codeCache + CODE_CACHE_SIZE, 0); + return true; +} + +bool dvmCompilerStartup(void) +{ + /* Make sure the BBType enum is in sane state */ + assert(CHAINING_CELL_GENERIC == 0); + + /* Architecture-specific chores to initialize */ + if (!dvmCompilerArchInit()) + goto fail; + + /* + * Setup the code cache if it is not done so already. For apps it should be + * done by the Zygote already, but for command-line dalvikvm invocation we + * need to do it here. + */ + if (gDvmJit.codeCache == NULL) { + if (!dvmCompilerSetupCodeCache()) + goto fail; + } + + /* Allocate the initial arena block */ + if (dvmCompilerHeapInit() == false) { + goto fail; + } + + dvmInitMutex(&gDvmJit.compilerLock); + pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL); + pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL); + + dvmLockMutex(&gDvmJit.compilerLock); + + gDvmJit.haltCompilerThread = false; + + /* Reset the work queue */ + memset(gDvmJit.compilerWorkQueue, 0, + sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE); + gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0; + gDvmJit.compilerQueueLength = 0; + gDvmJit.compilerHighWater = + COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4); + + assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE); + if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler", + compilerThreadStart, NULL)) { + dvmUnlockMutex(&gDvmJit.compilerLock); + goto fail; + } + + dvmUnlockMutex(&gDvmJit.compilerLock); + + return true; + +fail: + return false; +} + +void dvmCompilerShutdown(void) +{ + void *threadReturn; + + if (gDvmJit.compilerHandle) { + + gDvmJit.haltCompilerThread = true; + + dvmLockMutex(&gDvmJit.compilerLock); + pthread_cond_signal(&gDvmJit.compilerQueueActivity); + dvmUnlockMutex(&gDvmJit.compilerLock); + + pthread_join(gDvmJit.compilerHandle, &threadReturn); + } +} diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h new file mode 100644 index 000000000..720970124 --- /dev/null +++ b/vm/compiler/Compiler.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 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 _DALVIK_VM_COMPILER +#define _DALVIK_VM_COMPILER + +#define CODE_CACHE_SIZE 1024*1024 +#define MAX_JIT_RUN_LEN 64 +#define COMPILER_WORK_QUEUE_SIZE 100 + +#define COMPILER_TRACED(X) +#define COMPILER_TRACEE(X) +#define COMPILER_TRACE_CHAINING(X) + +typedef enum WorkOrderKind { + kWorkOrderInvalid = 0, // Should never see by the backend + kWorkOrderMethod = 1, // Work is to compile a whole method + kWorkOrderTrace = 2, // Work is to compile code fragment(s) +} WorkOrderKind; + +typedef struct CompilerWorkOrder { + const u2* pc; + WorkOrderKind kind; + void* info; +} CompilerWorkOrder; + +typedef enum JitState { + kJitOff = 0, + kJitNormal = 1, // Profiling in mterp or running native + kJitTSelectRequest = 2, // Transition state - start trace selection + kJitTSelect = 3, // Actively selecting trace in dbg interp + kJitTSelectAbort = 4, // Something threw during selection - abort + kJitTSelectEnd = 5, // Done with the trace - wrap it up + kJitSingleStep = 6, // Single step interpretation + kJitSingleStepEnd = 7, // Done with single step, return to mterp +} JitState; + +typedef enum JitHint { + kJitHintNone = 0, + kJitHintTaken = 1, // Last inst in run was taken branch + kJitHintNotTaken = 2, // Last inst in run was not taken branch + kJitHintNoBias = 3, // Last inst in run was unbiased branch +} jitHint; + +/* + * Element of a Jit trace description. Describes a contiguous + * sequence of Dalvik byte codes, the last of which can be + * associated with a hint. + * Dalvik byte code + */ +typedef struct { + u2 startOffset; // Starting offset for trace run + unsigned numInsts:8; // Number of Byte codes in run + unsigned runEnd:1; // Run ends with last byte code + jitHint hint:7; // Hint to apply to final code of run +} JitCodeDesc; + +typedef union { + JitCodeDesc frag; + void* hint; +} JitTraceRun; + +/* + * Trace description as will appear in the translation cache. Note + * flexible array at end, as these will be of variable size. To + * conserve space in the translation cache, total length of JitTraceRun + * array must be recomputed via seqential scan if needed. + */ +typedef struct { + const Method* method; + JitTraceRun trace[]; +} JitTraceDescription; + +bool dvmCompilerSetupCodeCache(void); +bool dvmCompilerArchInit(void); +void dvmCompilerArchDump(void); +bool dvmCompilerStartup(void); +void dvmCompilerShutdown(void); +bool dvmCompilerWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info); +void *dvmCheckCodeCache(void *method); +void *dvmCompileMethod(Method *method); +void *dvmCompileTrace(JitTraceDescription *trace); +void dvmCompilerDumpStats(void); +void dvmCompilerDrainQueue(void); + +#endif /* _DALVIK_VM_COMPILER */ diff --git a/vm/compiler/CompilerIR.h b/vm/compiler/CompilerIR.h new file mode 100644 index 000000000..6ffdf446f --- /dev/null +++ b/vm/compiler/CompilerIR.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009 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 _DALVIK_VM_COMPILER_IR +#define _DALVIK_VM_COMPILER_IR + +typedef enum BBType { + /* For coding convenience reasons chaining cell types should appear first */ + CHAINING_CELL_GENERIC = 0, + CHAINING_CELL_POST_INVOKE, + CHAINING_CELL_INVOKE, + CHAINING_CELL_LAST, + DALVIK_BYTECODE, + PC_RECONSTRUCTION, + EXCEPTION_HANDLING, +} BBType; + +typedef struct LIR { + int offset; + struct LIR *next; + struct LIR *prev; + struct LIR *target; +} LIR; + +typedef struct MIR { + DecodedInstruction dalvikInsn; + unsigned int width; + unsigned int offset; + struct MIR *prev; + struct MIR *next; +} MIR; + +typedef struct BasicBlock { + int id; + int visited; + unsigned int startOffset; + const Method *containingMethod; // For blocks from the callee + BBType blockType; + MIR *firstMIRInsn; + MIR *lastMIRInsn; + struct BasicBlock *fallThrough; + struct BasicBlock *taken; + struct BasicBlock *next; // Serial link for book keeping purposes +} BasicBlock; + +typedef struct CompilationUnit { + int numBlocks; + BasicBlock **blockList; + const Method *method; + const JitTraceDescription *traceDesc; + LIR *firstLIRInsn; + LIR *lastLIRInsn; + LIR *wordList; + GrowableList pcReconstructionList; + int dataOffset; + int totalSize; + unsigned char *codeBuffer; + void *baseAddr; + bool printMe; + bool allSingleStep; + int numChainingCells[CHAINING_CELL_LAST]; + LIR *firstChainingLIR[CHAINING_CELL_LAST]; +} CompilationUnit; + +BasicBlock *dvmCompilerNewBB(BBType blockType); + +void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir); + +void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir); + +/* Debug Utilities */ +void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit); + +#endif /* _DALVIK_VM_COMPILER_IR */ diff --git a/vm/compiler/CompilerInternals.h b/vm/compiler/CompilerInternals.h new file mode 100644 index 000000000..410213aac --- /dev/null +++ b/vm/compiler/CompilerInternals.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009 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 _DALVIK_VM_COMPILER_INTERNAL +#define _DALVIK_VM_COMPILER_INTERNAL + +#include "Dalvik.h" +#include "CompilerUtility.h" +#include "CompilerIR.h" +#include "codegen/CompilerCodegen.h" +#include "interp/Jit.h" + +#endif /* _DALVIK_VM_COMPILER_INTERNAL */ diff --git a/vm/compiler/CompilerUtility.h b/vm/compiler/CompilerUtility.h new file mode 100644 index 000000000..7b4de1199 --- /dev/null +++ b/vm/compiler/CompilerUtility.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 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 _DALVIK_VM_COMPILER_UTILITY +#define _DALVIK_VM_COMPILER_UTILITY + +#define ARENA_DEFAULT_SIZE 4096 + +/* Allocate the initial memory block for arena-based allocation */ +bool dvmCompilerHeapInit(void); + +typedef struct ArenaMemBlock { + size_t bytesAllocated; + struct ArenaMemBlock *next; + char ptr[0]; +} ArenaMemBlock; + +void *dvmCompilerNew(size_t size, bool zero); + +void dvmCompilerArenaReset(void); + +typedef struct GrowableList { + size_t numAllocated; + size_t numUsed; + void **elemList; +} GrowableList; + +void dvmInitGrowableList(GrowableList *gList, size_t initLength); +void dvmInsertGrowableList(GrowableList *gList, void *elem); + +#endif /* _DALVIK_COMPILER_UTILITY */ diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c new file mode 100644 index 000000000..59a745584 --- /dev/null +++ b/vm/compiler/Frontend.c @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "Dalvik.h" +#include "libdex/OpCode.h" +#include "dexdump/OpCodeNames.h" +#include "interp/Jit.h" +#include "CompilerInternals.h" + +/* + * Parse an instruction, return the length of the instruction + */ +static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn, + bool printMe) +{ + u2 instr = *codePtr; + OpCode opcode = instr & 0xff; + int insnWidth; + + // Need to check if this is a real NOP or a pseudo opcode + if (opcode == OP_NOP && instr != 0) { + if (instr == kPackedSwitchSignature) { + insnWidth = 4 + codePtr[1] * 2; + } else if (instr == kSparseSwitchSignature) { + insnWidth = 2 + codePtr[1] * 4; + } else if (instr == kArrayDataSignature) { + int width = codePtr[1]; + int size = codePtr[2] | (codePtr[3] << 16); + // The plus 1 is to round up for odd size and width + insnWidth = 4 + ((size * width) + 1) / 2; + } + insnWidth = 0; + } else { + insnWidth = gDvm.instrWidth[opcode]; + if (insnWidth < 0) { + insnWidth = -insnWidth; + } + } + + dexDecodeInstruction(gDvm.instrFormat, codePtr, decInsn); + if (printMe) { + LOGD("%p: %#06x %s\n", codePtr, opcode, getOpcodeName(opcode)); + } + return insnWidth; +} + +/* + * Identify block-ending instructions and collect supplemental information + * regarding the following instructions. + */ +static inline bool findBlockBoundary(const Method *caller, MIR *insn, + unsigned int curOffset, + unsigned int *target, bool *isInvoke, + const Method **callee) +{ + switch (insn->dalvikInsn.opCode) { + /* Target is not compile-time constant */ + case OP_RETURN_VOID: + case OP_RETURN: + case OP_RETURN_WIDE: + case OP_RETURN_OBJECT: + case OP_THROW: + case OP_INVOKE_VIRTUAL: + case OP_INVOKE_VIRTUAL_RANGE: + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: + case OP_INVOKE_VIRTUAL_QUICK: + case OP_INVOKE_VIRTUAL_QUICK_RANGE: + *isInvoke = true; + break; + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: { + int mIndex = caller->clazz->pDvmDex-> + pResMethods[insn->dalvikInsn.vB]->methodIndex; + const Method *calleeMethod = + caller->clazz->super->vtable[mIndex]; + + if (!dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: { + const Method *calleeMethod = + caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB]; + + if (!dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_INVOKE_SUPER_QUICK: + case OP_INVOKE_SUPER_QUICK_RANGE: { + const Method *calleeMethod = + caller->clazz->super->vtable[insn->dalvikInsn.vB]; + + if (!dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: { + const Method *calleeMethod = + caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB]; + if (!dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_GOTO: + case OP_GOTO_16: + case OP_GOTO_32: + *target = curOffset + (int) insn->dalvikInsn.vA; + break; + + case OP_IF_EQ: + case OP_IF_NE: + case OP_IF_LT: + case OP_IF_GE: + case OP_IF_GT: + case OP_IF_LE: + *target = curOffset + (int) insn->dalvikInsn.vC; + break; + + case OP_IF_EQZ: + case OP_IF_NEZ: + case OP_IF_LTZ: + case OP_IF_GEZ: + case OP_IF_GTZ: + case OP_IF_LEZ: + *target = curOffset + (int) insn->dalvikInsn.vB; + break; + + default: + return false; + } return true; +} + +/* + * Identify conditional branch instructions + */ +static inline bool isUnconditionalBranch(MIR *insn) +{ + switch (insn->dalvikInsn.opCode) { + case OP_RETURN_VOID: + case OP_RETURN: + case OP_RETURN_WIDE: + case OP_RETURN_OBJECT: + case OP_GOTO: + case OP_GOTO_16: + case OP_GOTO_32: + return true; + default: + return false; + } +} + +/* + * Main entry point to start trace compilation. Basic blocks are constructed + * first and they will be passed to the codegen routines to convert Dalvik + * bytecode into machine code. + */ +void *dvmCompileTrace(JitTraceDescription *desc) +{ + const DexCode *dexCode = dvmGetMethodCode(desc->method); + const JitTraceRun* currRun = &desc->trace[0]; + bool done = false; + unsigned int curOffset = currRun->frag.startOffset; + unsigned int numInsts = currRun->frag.numInsts; + const u2 *codePtr = dexCode->insns + curOffset; + int traceSize = 0; + const u2 *startCodePtr = codePtr; + BasicBlock *startBB, *curBB, *lastBB; + int numBlocks = 0; + static int compilationId; + CompilationUnit cUnit; + memset(&cUnit, 0, sizeof(CompilationUnit)); + + /* Initialize the printMe flag */ + cUnit.printMe = gDvmJit.printMe; + + /* Identify traces that we don't want to compile */ + if (gDvmJit.methodTable) { + int len = strlen(desc->method->clazz->descriptor) + + strlen(desc->method->name) + 1; + char *fullSignature = dvmCompilerNew(len, true); + strcpy(fullSignature, desc->method->clazz->descriptor); + strcat(fullSignature, desc->method->name); + + int hashValue = dvmComputeUtf8Hash(fullSignature); + + /* + * Doing three levels of screening to see whether we want to skip + * compiling this method + */ + + /* First, check the full "class;method" signature */ + bool methodFound = + dvmHashTableLookup(gDvmJit.methodTable, hashValue, + fullSignature, (HashCompareFunc) strcmp, + false) != + NULL; + + /* Full signature not found - check the enclosing class */ + if (methodFound == false) { + int hashValue = dvmComputeUtf8Hash(desc->method->clazz->descriptor); + methodFound = + dvmHashTableLookup(gDvmJit.methodTable, hashValue, + (char *) desc->method->clazz->descriptor, + (HashCompareFunc) strcmp, false) != + NULL; + /* Enclosing class not found - check the method name */ + if (methodFound == false) { + int hashValue = dvmComputeUtf8Hash(desc->method->name); + methodFound = + dvmHashTableLookup(gDvmJit.methodTable, hashValue, + (char *) desc->method->name, + (HashCompareFunc) strcmp, false) != + NULL; + } + } + + /* + * Under the following conditions, the trace will be *conservatively* + * compiled by only containing single-step instructions to and from the + * interpreter. + * 1) If includeSelectedMethod == false, the method matches the full or + * partial signature stored in the hash table. + * + * 2) If includeSelectedMethod == true, the method does not match the + * full and partial signature stored in the hash table. + */ + if (gDvmJit.includeSelectedMethod != methodFound) { + cUnit.allSingleStep = true; + } else { + /* Compile the trace as normal */ + + /* Print the method we cherry picked */ + if (gDvmJit.includeSelectedMethod == true) { + cUnit.printMe = true; + } + } + } + + /* Allocate the first basic block */ + lastBB = startBB = curBB = dvmCompilerNewBB(DALVIK_BYTECODE); + curBB->startOffset = curOffset; + curBB->id = numBlocks++; + + if (cUnit.printMe) { + LOGD("--------\nCompiler: Building trace for %s, offset 0x%x\n", + desc->method->name, curOffset); + } + + while (!done) { + MIR *insn; + int width; + insn = dvmCompilerNew(sizeof(MIR),false); + insn->offset = curOffset; + width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe); + insn->width = width; + traceSize += width; + dvmCompilerAppendMIR(curBB, insn); + if (--numInsts==0) { + if (currRun->frag.runEnd) { + done = true; + } else { + curBB = dvmCompilerNewBB(DALVIK_BYTECODE); + lastBB->next = curBB; + lastBB = curBB; + curBB->id = numBlocks++; + currRun++; + curOffset = currRun->frag.startOffset; + numInsts = currRun->frag.numInsts; + curBB->startOffset = curOffset; + codePtr = dexCode->insns + curOffset; + } + } else { + curOffset += width; + codePtr += width; + } + } + + /* + * Now scan basic blocks containing real code to connect the + * taken/fallthrough links. Also create chaining cells for code not included + * in the trace. + */ + for (curBB = startBB; curBB; curBB = curBB->next) { + MIR *lastInsn = curBB->lastMIRInsn; + /* Hit a pseudo block - exit the search now */ + if (lastInsn == NULL) { + break; + } + curOffset = lastInsn->offset; + unsigned int targetOffset = curOffset; + unsigned int fallThroughOffset = curOffset + lastInsn->width; + bool isInvoke = false; + const Method *callee = NULL; + + findBlockBoundary(desc->method, curBB->lastMIRInsn, curOffset, + &targetOffset, &isInvoke, &callee); + + /* Link the taken and fallthrough blocks */ + BasicBlock *searchBB; + + /* No backward branch in the trace - start searching the next BB */ + for (searchBB = curBB->next; searchBB; searchBB = searchBB->next) { + if (targetOffset == searchBB->startOffset) { + curBB->taken = searchBB; + } + if (fallThroughOffset == searchBB->startOffset) { + curBB->fallThrough = searchBB; + } + } + + /* Target block not included in the trace */ + if (targetOffset != curOffset && curBB->taken == NULL) { + lastBB->next = dvmCompilerNewBB( + isInvoke ? CHAINING_CELL_INVOKE : CHAINING_CELL_GENERIC); + lastBB = lastBB->next; + lastBB->id = numBlocks++; + if (isInvoke) { + lastBB->startOffset = 0; + lastBB->containingMethod = callee; + } else { + lastBB->startOffset = targetOffset; + } + curBB->taken = lastBB; + } + + /* Fallthrough block not included in the trace */ + if (!isUnconditionalBranch(lastInsn) && curBB->fallThrough == NULL) { + lastBB->next = dvmCompilerNewBB( + isInvoke ? CHAINING_CELL_POST_INVOKE : CHAINING_CELL_GENERIC); + lastBB = lastBB->next; + lastBB->id = numBlocks++; + lastBB->startOffset = fallThroughOffset; + curBB->fallThrough = lastBB; + } + } + + /* Now create a special block to host PC reconstruction code */ + lastBB->next = dvmCompilerNewBB(PC_RECONSTRUCTION); + lastBB = lastBB->next; + lastBB->id = numBlocks++; + + /* And one final block that publishes the PC and raise the exception */ + lastBB->next = dvmCompilerNewBB(EXCEPTION_HANDLING); + lastBB = lastBB->next; + lastBB->id = numBlocks++; + + if (cUnit.printMe) { + LOGD("TRACEINFO (%d): 0x%08x %s%s 0x%x %d of %d, %d blocks", + compilationId++, + (intptr_t) desc->method->insns, + desc->method->clazz->descriptor, + desc->method->name, + desc->trace[0].frag.startOffset, + traceSize, + dexCode->insnsSize, + numBlocks); + } + + BasicBlock **blockList; + + cUnit.method = desc->method; + cUnit.traceDesc = desc; + cUnit.numBlocks = numBlocks; + dvmInitGrowableList(&cUnit.pcReconstructionList, 8); + blockList = cUnit.blockList = + dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true); + + int i; + + for (i = 0, curBB = startBB; i < numBlocks; i++) { + blockList[i] = curBB; + curBB = curBB->next; + } + /* Make sure all blocks are added to the cUnit */ + assert(curBB == NULL); + + if (cUnit.printMe) { + dvmCompilerDumpCompilationUnit(&cUnit); + } + + /* Convert MIR to LIR, etc. */ + dvmCompilerMIR2LIR(&cUnit); + + /* Convert LIR into machine code */ + dvmCompilerAssembleLIR(&cUnit); + + if (cUnit.printMe) { + dvmCompilerCodegenDump(&cUnit); + LOGD("End %s%s", desc->method->clazz->descriptor, desc->method->name); + } + + /* Reset the compiler resource pool */ + dvmCompilerArenaReset(); + + return cUnit.baseAddr; +} + +/* + * Similar to dvmCompileTrace, but the entity processed here is the whole + * method. + * + * TODO: implementation will be revisited when the trace builder can provide + * whole-method traces. + */ +void *dvmCompileMethod(Method *method) +{ + const DexCode *dexCode = dvmGetMethodCode(method); + const u2 *codePtr = dexCode->insns; + const u2 *codeEnd = dexCode->insns + dexCode->insnsSize; + int blockID = 0; + unsigned int curOffset = 0; + + BasicBlock *firstBlock = dvmCompilerNewBB(DALVIK_BYTECODE); + firstBlock->id = blockID++; + + /* Allocate the bit-vector to track the beginning of basic blocks */ + BitVector *bbStartAddr = dvmAllocBitVector(dexCode->insnsSize+1, false); + dvmSetBit(bbStartAddr, 0); + + /* + * Sequentially go through every instruction first and put them in a single + * basic block. Identify block boundaries at the mean time. + */ + while (codePtr < codeEnd) { + MIR *insn = dvmCompilerNew(sizeof(MIR), false); + insn->offset = curOffset; + int width = parseInsn(codePtr, &insn->dalvikInsn, false); + bool isInvoke = false; + const Method *callee; + insn->width = width; + + dvmCompilerAppendMIR(firstBlock, insn); + /* + * Check whether this is a block ending instruction and whether it + * suggests the start of a new block + */ + unsigned int target = curOffset; + + /* + * If findBlockBoundary returns true, it means the current instruction + * is terminating the current block. If it is a branch, the target + * address will be recorded in target. + */ + if (findBlockBoundary(method, insn, curOffset, &target, &isInvoke, + &callee)) { + dvmSetBit(bbStartAddr, curOffset + width); + if (target != curOffset) { + dvmSetBit(bbStartAddr, target); + } + } + + codePtr += width; + /* each bit represents 16-bit quantity */ + curOffset += width; + } + + /* + * The number of blocks will be equal to the number of bits set to 1 in the + * bit vector minus 1, because the bit representing the location after the + * last instruction is set to one. + */ + int numBlocks = dvmCountSetBits(bbStartAddr); + if (dvmIsBitSet(bbStartAddr, dexCode->insnsSize)) { + numBlocks--; + } + + CompilationUnit cUnit; + BasicBlock **blockList; + + memset(&cUnit, 0, sizeof(CompilationUnit)); + cUnit.method = method; + blockList = cUnit.blockList = + dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true); + + /* + * Register the first block onto the list and start split it into block + * boundaries from there. + */ + blockList[0] = firstBlock; + cUnit.numBlocks = 1; + + int i; + for (i = 0; i < numBlocks; i++) { + MIR *insn; + BasicBlock *curBB = blockList[i]; + curOffset = curBB->lastMIRInsn->offset; + + for (insn = curBB->firstMIRInsn->next; insn; insn = insn->next) { + /* Found the beginning of a new block, see if it is created yet */ + if (dvmIsBitSet(bbStartAddr, insn->offset)) { + int j; + for (j = 0; j < cUnit.numBlocks; j++) { + if (blockList[j]->firstMIRInsn->offset == insn->offset) + break; + } + + /* Block not split yet - do it now */ + if (j == cUnit.numBlocks) { + BasicBlock *newBB = dvmCompilerNewBB(DALVIK_BYTECODE); + newBB->id = blockID++; + newBB->firstMIRInsn = insn; + newBB->lastMIRInsn = curBB->lastMIRInsn; + curBB->lastMIRInsn = insn->prev; + insn->prev->next = NULL; + insn->prev = NULL; + + /* + * If the insn is not an unconditional branch, set up the + * fallthrough link. + */ + if (!isUnconditionalBranch(curBB->lastMIRInsn)) { + curBB->fallThrough = newBB; + } + + /* enqueue the new block */ + blockList[cUnit.numBlocks++] = newBB; + break; + } + } + } + } + + if (numBlocks != cUnit.numBlocks) { + LOGE("Expect %d vs %d basic blocks\n", numBlocks, cUnit.numBlocks); + dvmAbort(); + } + + dvmFreeBitVector(bbStartAddr); + + /* Connect the basic blocks through the taken links */ + for (i = 0; i < numBlocks; i++) { + BasicBlock *curBB = blockList[i]; + MIR *insn = curBB->lastMIRInsn; + unsigned int target = insn->offset; + bool isInvoke; + const Method *callee; + + findBlockBoundary(method, insn, target, &target, &isInvoke, &callee); + + /* Found a block ended on a branch */ + if (target != insn->offset) { + int j; + /* Forward branch */ + if (target > insn->offset) { + j = i + 1; + } else { + /* Backward branch */ + j = 0; + } + for (; j < numBlocks; j++) { + if (blockList[j]->firstMIRInsn->offset == target) { + curBB->taken = blockList[j]; + break; + } + } + + if (j == numBlocks) { + LOGE("Target not found for insn %x: expect target %x\n", + curBB->lastMIRInsn->offset, target); + dvmAbort(); + } + } + } + + dvmCompilerMIR2LIR(&cUnit); + + dvmCompilerAssembleLIR(&cUnit); + + dvmCompilerDumpCompilationUnit(&cUnit); + + dvmCompilerArenaReset(); + + return cUnit.baseAddr; +} diff --git a/vm/compiler/IntermediateRep.c b/vm/compiler/IntermediateRep.c new file mode 100644 index 000000000..2596aab92 --- /dev/null +++ b/vm/compiler/IntermediateRep.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "Dalvik.h" +#include "CompilerInternals.h" + +/* Allocate a new basic block */ +BasicBlock *dvmCompilerNewBB(BBType blockType) +{ + BasicBlock *bb = dvmCompilerNew(sizeof(BasicBlock), true); + bb->blockType = blockType; + return bb; +} + +/* Insert an MIR instruction to the end of a basic block */ +void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir) +{ + if (bb->firstMIRInsn == NULL) { + assert(bb->firstMIRInsn == NULL); + bb->lastMIRInsn = bb->firstMIRInsn = mir; + mir->prev = mir->next = NULL; + } else { + bb->lastMIRInsn->next = mir; + mir->prev = bb->lastMIRInsn; + mir->next = NULL; + bb->lastMIRInsn = mir; + } +} + +/* + * Append an LIR instruction to the LIR list maintained by a compilation + * unit + */ +void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir) +{ + if (cUnit->firstLIRInsn == NULL) { + assert(cUnit->lastLIRInsn == NULL); + cUnit->lastLIRInsn = cUnit->firstLIRInsn = lir; + lir->prev = lir->next = NULL; + } else { + cUnit->lastLIRInsn->next = lir; + lir->prev = cUnit->lastLIRInsn; + lir->next = NULL; + cUnit->lastLIRInsn = lir; + } +} diff --git a/vm/compiler/Utility.c b/vm/compiler/Utility.c new file mode 100644 index 000000000..9d8e08861 --- /dev/null +++ b/vm/compiler/Utility.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "Dalvik.h" +#include "CompilerInternals.h" + +static ArenaMemBlock *arenaHead, *currentArena; +static int numArenaBlocks; + +/* Allocate the initial memory block for arena-based allocation */ +bool dvmCompilerHeapInit(void) +{ + assert(arenaHead == NULL); + arenaHead = + (ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE); + if (arenaHead == NULL) { + LOGE("No memory left to create compiler heap memory\n"); + return false; + } + currentArena = arenaHead; + currentArena->bytesAllocated = 0; + currentArena->next = NULL; + numArenaBlocks = 1; + + return true; +} + +/* Arena-based malloc for compilation tasks */ +void * dvmCompilerNew(size_t size, bool zero) +{ + size = (size + 3) & ~3; +retry: + /* Normal case - space is available in the current page */ + if (size + currentArena->bytesAllocated <= ARENA_DEFAULT_SIZE) { + void *ptr; + ptr = ¤tArena->ptr[currentArena->bytesAllocated]; + currentArena->bytesAllocated += size; + if (zero) { + memset(ptr, 0, size); + } + return ptr; + } else { + /* + * See if there are previously allocated arena blocks before the last + * reset + */ + if (currentArena->next) { + currentArena = currentArena->next; + goto retry; + } + /* + * If we allocate really large variable-sized data structures that + * could go above the limit we need to enhance the allocation + * mechanism. + */ + if (size > ARENA_DEFAULT_SIZE) { + LOGE("Requesting %d bytes which exceed the maximal size allowed\n", + size); + return NULL; + } + /* Time to allocate a new arena */ + ArenaMemBlock *newArena = (ArenaMemBlock *) + malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE); + newArena->bytesAllocated = 0; + newArena->next = NULL; + currentArena->next = newArena; + currentArena = newArena; + numArenaBlocks++; + goto retry; + } + return NULL; +} + +/* Reclaim all the arena blocks allocated so far */ +void dvmCompilerArenaReset(void) +{ + ArenaMemBlock *block; + + for (block = arenaHead; block; block = block->next) { + block->bytesAllocated = 0; + } + currentArena = arenaHead; +} + +/* Growable List initialization */ +void dvmInitGrowableList(GrowableList *gList, size_t initLength) +{ + gList->numAllocated = initLength; + gList->numUsed = 0; + gList->elemList = (void **) dvmCompilerNew(sizeof(void *) * initLength, + true); +} + +/* Expand the capacity of a growable list */ +static void expandGrowableList(GrowableList *gList) +{ + int newLength = gList->numAllocated; + if (newLength < 128) { + newLength <<= 1; + } else { + newLength += 128; + } + void *newArray = dvmCompilerNew(sizeof(void *) * newLength, true); + memcpy(newArray, gList->elemList, sizeof(void *) * gList->numAllocated); + gList->numAllocated = newLength; + gList->elemList = newArray; +} + +/* Insert a new element into the growable list */ +void dvmInsertGrowableList(GrowableList *gList, void *elem) +{ + if (gList->numUsed == gList->numAllocated) { + expandGrowableList(gList); + } + gList->elemList[gList->numUsed++] = elem; +} + +/* Debug Utility - dump a compilation unit */ +void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit) +{ + int i; + BasicBlock *bb; + LOGD("%d blocks in total\n", cUnit->numBlocks); + + for (i = 0; i < cUnit->numBlocks; i++) { + bb = cUnit->blockList[i]; + LOGD("Block %d (insn %04x - %04x%s)\n", + bb->id, bb->startOffset, + bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset, + bb->lastMIRInsn ? "" : " empty"); + if (bb->taken) { + LOGD(" Taken branch: block %d (%04x)\n", + bb->taken->id, bb->taken->startOffset); + } + if (bb->fallThrough) { + LOGD(" Fallthrough : block %d (%04x)\n", + bb->fallThrough->id, bb->fallThrough->startOffset); + } + } +} + +/* + * Dump the current stats of the compiler, including number of bytes used in + * the code cache, arena size, and work queue length, and various JIT stats. + */ +void dvmCompilerDumpStats(void) +{ + LOGD("%d compilations using %d bytes", + gDvmJit.numCompilations, gDvmJit.codeCacheByteUsed); + LOGD("Compiler arena uses %d blocks (%d bytes each)", + numArenaBlocks, ARENA_DEFAULT_SIZE); + LOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength, + gDvmJit.compilerMaxQueued); + dvmJitStats(); + dvmCompilerArchDump(); +} diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h new file mode 100644 index 000000000..97077b415 --- /dev/null +++ b/vm/compiler/codegen/CompilerCodegen.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "../CompilerIR.h" + +#ifndef _DALVIK_VM_COMPILERCODEGEN_H_ +#define _DALVIK_VM_COMPILERCODEGEN_H_ + +/* Work unit is architecture dependent */ +void *dvmCompilerDoWork(CompilerWorkOrder *work); + +/* Lower middle-level IR to low-level IR */ +void dvmCompilerMIR2LIR(CompilationUnit *cUnit); + +/* Assemble LIR into machine code */ +void dvmCompilerAssembleLIR(CompilationUnit *cUnit); + +/* Implemented in the codegen/<target>/ArchUtility.c */ +void dvmCompilerCodegenDump(CompilationUnit *cUnit); + +/* Implemented in the codegen/<target>/Assembler.c */ +void* dvmJitChain(void *tgtAddr, u4* branchAddr); + +#endif /* _DALVIK_VM_COMPILERCODEGEN_H_ */ diff --git a/vm/compiler/codegen/armv5te/ArchUtility.c b/vm/compiler/codegen/armv5te/ArchUtility.c new file mode 100644 index 000000000..58b181b7e --- /dev/null +++ b/vm/compiler/codegen/armv5te/ArchUtility.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "../../CompilerInternals.h" +#include "dexdump/OpCodeNames.h" +#include "Armv5teLIR.h" + +/* Decode and print a ARM register name */ +static char * decodeRegList(int vector, char *buf) +{ + int i; + bool printed = false; + buf[0] = 0; + for (i = 0; i < 8; i++, vector >>= 1) { + if (vector & 0x1) { + if (printed) { + sprintf(buf + strlen(buf), ", r%d", i); + } else { + printed = true; + sprintf(buf, "r%d", i); + } + } + } + return buf; +} + +/* + * Interpret a format string and build a string no longer than size + * See format key in Assemble.c. + */ +static void buildInsnString(char *fmt, Armv5teLIR *lir, char* buf, + unsigned char *baseAddr, int size) +{ + int i; + char *bufEnd = &buf[size-1]; + char *fmtEnd = &fmt[strlen(fmt)]; + char tbuf[256]; + char nc; + while (fmt < fmtEnd) { + int operand; + if (*fmt == '!') { + fmt++; + assert(fmt < fmtEnd); + nc = *fmt++; + if (nc=='!') { + strcpy(tbuf, "!"); + } else { + assert(fmt < fmtEnd); + assert((unsigned)(nc-'0') < 3); + operand = lir->operands[nc-'0']; + switch(*fmt++) { + case 'h': + sprintf(tbuf,"%04x", operand); + break; + case 'd': + sprintf(tbuf,"%d", operand); + break; + case 'D': + sprintf(tbuf,"%d", operand+8); + break; + case 'E': + sprintf(tbuf,"%d", operand*4); + break; + case 'F': + sprintf(tbuf,"%d", operand*2); + break; + case 'c': + switch (operand) { + case ARM_COND_EQ: + strcpy(tbuf, "beq"); + break; + case ARM_COND_NE: + strcpy(tbuf, "bne"); + break; + case ARM_COND_LT: + strcpy(tbuf, "blt"); + break; + case ARM_COND_GE: + strcpy(tbuf, "bge"); + break; + case ARM_COND_GT: + strcpy(tbuf, "bgt"); + break; + case ARM_COND_LE: + strcpy(tbuf, "ble"); + break; + case ARM_COND_CS: + strcpy(tbuf, "bcs"); + break; + default: + strcpy(tbuf, ""); + break; + } + break; + case 't': + sprintf(tbuf,"0x%08x", + (int) baseAddr + lir->generic.offset + 4 + + (operand << 1)); + break; + case 'u': { + int offset_1 = lir->operands[0]; + int offset_2 = NEXT_LIR(lir)->operands[0]; + intptr_t target = + ((((intptr_t) baseAddr + lir->generic.offset + 4) & + ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) & + 0xfffffffc; + sprintf(tbuf, "%p", (void *) target); + break; + } + + /* Nothing to print for BLX_2 */ + case 'v': + strcpy(tbuf, "see above"); + break; + case 'R': + decodeRegList(operand, tbuf); + break; + default: + strcpy(tbuf,"DecodeError"); + break; + } + if (buf+strlen(tbuf) <= bufEnd) { + strcpy(buf, tbuf); + buf += strlen(tbuf); + } else { + break; + } + } + } else { + *buf++ = *fmt++; + } + if (buf == bufEnd) + break; + } + *buf = 0; +} + +/* Pretty-print a LIR instruction */ +static void dumpLIRInsn(LIR *arg, unsigned char *baseAddr) +{ + Armv5teLIR *lir = (Armv5teLIR *) arg; + char buf[256]; + char opName[256]; + int offset = lir->generic.offset; + int dest = lir->operands[0]; + u2 *cPtr = (u2*)baseAddr; + /* Handle pseudo-ops individually, and all regular insns as a group */ + switch(lir->opCode) { + case ARMV5TE_PSEUDO_TARGET_LABEL: + break; + case ARMV5TE_PSEUDO_CHAINING_CELL_GENERIC: + LOGD("-------- chaining cell (generic): 0x%04x\n", dest); + break; + case ARMV5TE_PSEUDO_CHAINING_CELL_POST_INVOKE: + LOGD("-------- chaining cell (post-invoke): 0x%04x\n", dest); + break; + case ARMV5TE_PSEUDO_CHAINING_CELL_INVOKE: + LOGD("-------- chaining cell (invoke): %s/%p\n", + ((Method *)dest)->name, + ((Method *)dest)->insns); + break; + case ARMV5TE_PSEUDO_DALVIK_BYTECODE_BOUNDARY: + LOGD("-------- dalvik offset: 0x%04x @ %s\n", dest, + getOpcodeName(lir->operands[1])); + break; + case ARMV5TE_PSEUDO_ALIGN4: + LOGD("%p (%04x): .align4\n", baseAddr + offset, offset); + break; + case ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL: + LOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x\n", dest, + lir->operands[1]); + break; + case ARMV5TE_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL: + /* Do nothing */ + break; + case ARMV5TE_PSEUDO_EH_BLOCK_LABEL: + LOGD("Exception_Handling:\n"); + break; + case ARMV5TE_PSEUDO_NORMAL_BLOCK_LABEL: + LOGD("L%#06x:\n", dest); + break; + default: + buildInsnString(EncodingMap[lir->opCode].name, lir, opName, + baseAddr, 256); + buildInsnString(EncodingMap[lir->opCode].fmt, lir, buf, baseAddr, + 256); + LOGD("%p (%04x): %-8s%s\n", baseAddr + offset, offset, opName, buf); + break; + } +} + +/* Dump instructions and constant pool contents */ +void dvmCompilerCodegenDump(CompilationUnit *cUnit) +{ + LOGD("Dumping LIR insns\n"); + LIR *lirInsn; + Armv5teLIR *armLIR; + + LOGD("installed code is at %p\n", cUnit->baseAddr); + LOGD("total size is %d bytes\n", cUnit->totalSize); + for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) { + dumpLIRInsn(lirInsn, cUnit->baseAddr); + } + for (lirInsn = cUnit->wordList; lirInsn; lirInsn = lirInsn->next) { + armLIR = (Armv5teLIR *) lirInsn; + LOGD("%p (%04x): .word (0x%x)\n", + cUnit->baseAddr + armLIR->generic.offset, armLIR->generic.offset, + armLIR->operands[0]); + } +} diff --git a/vm/compiler/codegen/armv5te/Armv5teLIR.h b/vm/compiler/codegen/armv5te/Armv5teLIR.h new file mode 100644 index 000000000..208e6c0eb --- /dev/null +++ b/vm/compiler/codegen/armv5te/Armv5teLIR.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "Dalvik.h" +#include "compiler/CompilerInternals.h" + +#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARMV5TE_H +#define _DALVIK_VM_COMPILER_CODEGEN_ARMV5TE_H + +/* + * r0, r1, r2, r3, and r7 are always scratch + * r4PC is scratch if used solely in the compiled land. Otherwise it holds the + * Dalvik PC. + * rFP holds the current frame pointer + * rGLUE holds &InterpState + */ +typedef enum NativeRegisterPool { + r0 = 0, + r1 = 1, + r2 = 2, + r3 = 3, + r4PC = 4, + rFP = 5, + rGLUE = 6, + r7 = 7, +} NativeRegisterPool; + +/* Thumb condition encodings */ +typedef enum Armv5teConditionCode { + ARM_COND_EQ = 0x0, /* 0000 */ + ARM_COND_NE = 0x1, /* 0001 */ + ARM_COND_LT = 0xb, /* 1011 */ + ARM_COND_GE = 0xa, /* 1010 */ + ARM_COND_GT = 0xc, /* 1100 */ + ARM_COND_LE = 0xd, /* 1101 */ + ARM_COND_CS = 0x2, /* 0010 */ + ARM_COND_MI = 0x4, /* 0100 */ +} Armv5teConditionCode; + +#define isPseudoOpCode(opCode) ((int)(opCode) < 0) + +/* + * The following enum defines the list of supported Thumb instructions by the + * assembler. Their corresponding snippet positions will be defined in + * Assemble.c. + */ +typedef enum Armv5teOpCode { + ARMV5TE_PSEUDO_TARGET_LABEL = -10, + ARMV5TE_PSEUDO_CHAINING_CELL_POST_INVOKE = -9, + ARMV5TE_PSEUDO_CHAINING_CELL_INVOKE = -8, + ARMV5TE_PSEUDO_CHAINING_CELL_GENERIC = -7, + ARMV5TE_PSEUDO_DALVIK_BYTECODE_BOUNDARY = -6, + ARMV5TE_PSEUDO_ALIGN4 = -5, + ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL = -4, + ARMV5TE_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL = -3, + ARMV5TE_PSEUDO_EH_BLOCK_LABEL = -2, + ARMV5TE_PSEUDO_NORMAL_BLOCK_LABEL = -1, + /************************************************************************/ + ARMV5TE_16BIT_DATA, /* DATA [0] rd[15..0] */ + ARMV5TE_ADC, /* adc [0100000101] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_RRI3, /* add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/ + ARMV5TE_ADD_RI8, /* add(2) [00110] rd[10..8] imm_8[7..0] */ + ARMV5TE_ADD_RRR, /* add(3) [0001100] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_ADD_RR_LH, /* add(4) [01000100] H12[01] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_RR_HL, /* add(4) [01001000] H12[10] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_RR_HH, /* add(4) [01001100] H12[11] rm[5..3] rd[2..0] */ + ARMV5TE_ADD_PC_REL, /* add(5) [10100] rd[10..8] imm_8[7..0] */ + ARMV5TE_ADD_SP_REL, /* add(6) [10101] rd[10..8] imm_8[7..0] */ + ARMV5TE_ADD_SPI7, /* add(7) [101100000] imm_7[6..0] */ + ARMV5TE_AND_RR, /* and [0100000000] rm[5..3] rd[2..0] */ + ARMV5TE_ASR, /* asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0] */ + ARMV5TE_ASRV, /* asr(2) [0100000100] rs[5..3] rd[2..0] */ + ARMV5TE_B_COND, /* b(1) [1101] cond[11..8] offset_8[7..0] */ + ARMV5TE_B_UNCOND, /* b(2) [11100] offset_11[10..0] */ + ARMV5TE_BIC, /* bic [0100001110] rm[5..3] rd[2..0] */ + ARMV5TE_BKPT, /* bkpt [10111110] imm_8[7..0] */ + ARMV5TE_BLX_1, /* blx(1) [111] H[10] offset_11[10..0] */ + ARMV5TE_BLX_2, /* blx(1) [111] H[01] offset_11[10..0] */ + ARMV5TE_BL_1, /* blx(1) [111] H[10] offset_11[10..0] */ + ARMV5TE_BL_2, /* blx(1) [111] H[11] offset_11[10..0] */ + ARMV5TE_BLX_R, /* blx(2) [010001111] H2[6..6] rm[5..3] SBZ[000] */ + ARMV5TE_BX, /* bx [010001110] H2[6..6] rm[5..3] SBZ[000] */ + ARMV5TE_CMN, /* cmn [0100001011] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_RI8, /* cmp(1) [00101] rn[10..8] imm_8[7..0] */ + ARMV5TE_CMP_RR, /* cmp(2) [0100001010] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_LH, /* cmp(3) [01000101] H12[01] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_HL, /* cmp(3) [01000110] H12[10] rm[5..3] rd[2..0] */ + ARMV5TE_CMP_HH, /* cmp(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + ARMV5TE_EOR, /* eor [0100000001] rm[5..3] rd[2..0] */ + ARMV5TE_LDMIA, /* ldmia [11001] rn[10..8] reglist [7..0] */ + ARMV5TE_LDR_RRI5, /* ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDR_RRR, /* ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDR_PC_REL, /* ldr(3) [01001] rd[10..8] imm_8[7..0] */ + ARMV5TE_LDR_SP_REL, /* ldr(4) [10011] rd[10..8] imm_8[7..0] */ + ARMV5TE_LDRB_RRI5, /* ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRB_RRR, /* ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRH_RRI5, /* ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRH_RRR, /* ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRSB_RRR, /* ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LDRSH_RRR, /* ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_LSL, /* lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0] */ + ARMV5TE_LSLV, /* lsl(2) [0100000010] rs[5..3] rd[2..0] */ + ARMV5TE_LSR, /* lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0] */ + ARMV5TE_LSRV, /* lsr(2) [0100000011] rs[5..3] rd[2..0] */ + ARMV5TE_MOV_IMM, /* mov(1) [00100] rd[10..8] imm_8[7..0] */ + ARMV5TE_MOV_RR, /* mov(2) [0001110000] rn[5..3] rd[2..0] */ + ARMV5TE_MOV_RR_HL, /* mov(3) [01000110] H12[10] rm[5..3] rd[2..0] */ + ARMV5TE_MOV_RR_LH, /* mov(3) [01000101] H12[01] rm[5..3] rd[2..0] */ + ARMV5TE_MOV_RR_HH, /* mov(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + ARMV5TE_MUL, /* mul [0100001101] rm[5..3] rd[2..0] */ + ARMV5TE_MVN, /* mvn [0100001111] rm[5..3] rd[2..0] */ + ARMV5TE_NEG, /* neg [0100001001] rm[5..3] rd[2..0] */ + ARMV5TE_ORR, /* orr [0100001100] rm[5..3] rd[2..0] */ + ARMV5TE_POP, /* pop [1011110] r[8..8] rl[7..0] */ + ARMV5TE_PUSH, /* push [1011010] r[8..8] rl[7..0] */ + ARMV5TE_ROR, /* ror [0100000111] rs[5..3] rd[2..0] */ + ARMV5TE_SBC, /* sbc [0100000110] rm[5..3] rd[2..0] */ + ARMV5TE_STMIA, /* stmia [11000] rn[10..8] reglist [7.. 0] */ + ARMV5TE_STR_RRI5, /* str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_STR_RRR, /* str(2) [0101000] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_STR_SP_REL, /* str(3) [10010] rd[10..8] imm_8[7..0] */ + ARMV5TE_STRB_RRI5, /* strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_STRB_RRR, /* strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_STRH_RRI5, /* strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0] */ + ARMV5TE_STRH_RRR, /* strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_SUB_RRI3, /* sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/ + ARMV5TE_SUB_RI8, /* sub(2) [00111] rd[10..8] imm_8[7..0] */ + ARMV5TE_SUB_RRR, /* sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0] */ + ARMV5TE_SUB_SPI7, /* sub(4) [101100001] imm_7[6..0] */ + ARMV5TE_SWI, /* swi [11011111] imm_8[7..0] */ + ARMV5TE_TST, /* tst [0100001000] rm[5..3] rn[2..0] */ + ARMV5TE_LAST, +} Armv5teOpCode; + +/* Struct used to define the snippet posotions for each Thumb opcode */ +typedef struct Armv5teEncodingMap { + short skeleton; + struct { + int end; + int start; + } fieldLoc[3]; + Armv5teOpCode opCode; + int operands; + char *name; + char* fmt; +} Armv5teEncodingMap; + +extern Armv5teEncodingMap EncodingMap[ARMV5TE_LAST]; + +/* + * Each instance of this struct holds a pseudo or real LIR instruction: + * - pesudo ones (eg labels and marks) and will be discarded by the assembler. + * - real ones will e assembled into Thumb instructions. + */ +typedef struct Armv5teLIR { + LIR generic; + Armv5teOpCode opCode; + int operands[3]; /* dest, src1, src2 */ +} Armv5teLIR; + +/* Utility macros to traverse the LIR/Armv5teLIR list */ +#define NEXT_LIR(lir) ((Armv5teLIR *) lir->generic.next) +#define PREV_LIR(lir) ((Armv5teLIR *) lir->generic.prev) + +#define NEXT_LIR_LVALUE(lir) (lir)->generic.next +#define PREV_LIR_LVALUE(lir) (lir)->generic.prev + +#endif /* _DALVIK_VM_COMPILER_CODEGEN_ARMV5TE_H */ diff --git a/vm/compiler/codegen/armv5te/Assemble.c b/vm/compiler/codegen/armv5te/Assemble.c new file mode 100644 index 000000000..14355cb28 --- /dev/null +++ b/vm/compiler/codegen/armv5te/Assemble.c @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "Dalvik.h" +#include "libdex/OpCode.h" +#include "dexdump/OpCodeNames.h" + +#include "../../CompilerInternals.h" +#include "Armv5teLIR.h" +#include <unistd.h> /* for cacheflush */ + +/* + * opcode: Armv5teOpCode enum + * skeleton: pre-designated bit-pattern for this opcode + * ds: dest start bit position + * de: dest end bit position + * s1s: src1 start bit position + * s1e: src1 end bit position + * s2s: src2 start bit position + * s2e: src2 end bit position + * operands: number of operands (for sanity check purposes) + * name: mnemonic name + * fmt: for pretty-prining + */ +#define ENCODING_MAP(opcode, skeleton, ds, de, s1s, s1e, s2s, s2e, operands, \ + name, fmt) \ + {skeleton, {{ds, de}, {s1s, s1e}, {s2s, s2e}}, opcode, operands, name, \ + fmt} + +/* Instruction dump string format keys: !pf, where "!" is the start + * of the key, "p" is which numeric operand to use and "f" is the + * print format. + * + * [p]ositions: + * 0 -> operands[0] (dest) + * 1 -> operands[1] (src1) + * 2 -> operands[2] (src2) + * + * [f]ormats: + * h -> 4-digit hex + * d -> decimal + * D -> decimal+8 (used to convert 3-bit regnum field to high reg) + * E -> decimal*4 + * F -> decimal*2 + * c -> branch condition (beq, bne, etc.) + * t -> pc-relative target + * u -> 1st half of bl[x] target + * v -> 2nd half ob bl[x] target + * R -> register list + * + * [!] escape. To insert "!", use "!!" + */ +/* NOTE: must be kept in sync with enum Armv5teOpcode from Armv5teLIR.h */ +Armv5teEncodingMap EncodingMap[ARMV5TE_LAST] = { + ENCODING_MAP(ARMV5TE_16BIT_DATA, 0x0000, 15, 0, -1, -1, -1, -1, + 1, "data", "0x!0h(!0d)"), + ENCODING_MAP(ARMV5TE_ADC, 0x4140, 2, 0, 5, 3, -1, -1, + 2, "adc", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_ADD_RRI3, 0x1c00, 2, 0, 5, 3, 8, 6, + 3, "add", "r!0d, r!1d, #!2d"), + ENCODING_MAP(ARMV5TE_ADD_RI8, 0x3000, 10, 8, 7, 0, -1, -1, + 2, "add", "r!0d, r!0d, #!1d"), + ENCODING_MAP(ARMV5TE_ADD_RRR, 0x1800, 2, 0, 5, 3, 8, 6, + 3, "add", "r!0d, r!1d, r!2d"), + ENCODING_MAP(ARMV5TE_ADD_RR_LH, 0x4440, 2, 0, 5, 3, -1, -1, + 2, "add", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_ADD_RR_HL, 0x4480, 2, 0, 5, 3, -1, -1, + 2, "add", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_ADD_RR_HH, 0x44c0, 2, 0, 5, 3, -1, -1, + 2, "add", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_ADD_PC_REL, 0xa000, 10, 8, 7, 0, -1, -1, + 2, "add", "r!0d, pc, #!1E"), + ENCODING_MAP(ARMV5TE_ADD_SP_REL, 0xa800, 10, 8, 7, 0, -1, -1, + 2, "add", "r!0d, sp, #!1E"), + ENCODING_MAP(ARMV5TE_ADD_SPI7, 0xb000, 6, 0, -1, -1, -1, -1, + 1, "add", "sp, #!0d*4"), + ENCODING_MAP(ARMV5TE_AND_RR, 0x4000, 2, 0, 5, 3, -1, -1, + 2, "and", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_ASR, 0x1000, 2, 0, 5, 3, 10, 6, + 3, "asr", "r!0d, r!1d, #!2d"), + ENCODING_MAP(ARMV5TE_ASRV, 0x4100, 2, 0, 5, 3, -1, -1, + 2, "asr", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_B_COND, 0xd000, 7, 0, 11, 8, -1, -1, + 2, "!1c", "!0t"), + ENCODING_MAP(ARMV5TE_B_UNCOND, 0xe000, 10, 0, -1, -1, -1, -1, + 0, "b", "!0t"), + ENCODING_MAP(ARMV5TE_BIC, 0x4380, 2, 0, 5, 3, -1, -1, + 2, "bic", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_BKPT, 0xbe00, 7, 0, -1, -1, -1, -1, + 1, "bkpt", "!0d"), + ENCODING_MAP(ARMV5TE_BLX_1, 0xf000, 10, 0, -1, -1, -1, -1, + 2, "blx_1", "!0u"), + ENCODING_MAP(ARMV5TE_BLX_2, 0xe800, 10, 0, -1, -1, -1, -1, + 2, "blx_2", "!0v"), + ENCODING_MAP(ARMV5TE_BL_1, 0xf000, 10, 0, -1, -1, -1, -1, + 1, "bl_1", "!0u"), + ENCODING_MAP(ARMV5TE_BL_2, 0xf800, 10, 0, -1, -1, -1, -1, + 1, "bl_2", "!0v"), + ENCODING_MAP(ARMV5TE_BLX_R, 0x4780, 6, 3, -1, -1, -1, -1, + 1, "blx", "r!0d"), + ENCODING_MAP(ARMV5TE_BX, 0x4700, 6, 3, -1, -1, -1, -1, + 1, "bx", "r!0d"), + ENCODING_MAP(ARMV5TE_CMN, 0x42c0, 2, 0, 5, 3, -1, -1, + 2, "cmn", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_CMP_RI8, 0x2800, 10, 8, 7, 0, -1, -1, + 2, "cmp", "r!0d, #!1d"), + ENCODING_MAP(ARMV5TE_CMP_RR, 0x4280, 2, 0, 5, 3, -1, -1, + 2, "cmp", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_CMP_LH, 0x4540, 2, 0, 5, 3, -1, -1, + 2, "cmp", "r!0d, r!1D"), + ENCODING_MAP(ARMV5TE_CMP_HL, 0x4580, 2, 0, 5, 3, -1, -1, + 2, "cmp", "r!0D, r!1d"), + ENCODING_MAP(ARMV5TE_CMP_HH, 0x45c0, 2, 0, 5, 3, -1, -1, + 2, "cmp", "r!0D, r!1D"), + ENCODING_MAP(ARMV5TE_EOR, 0x4040, 2, 0, 5, 3, -1, -1, + 2, "eor", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_LDMIA, 0xc800, 10, 8, 7, 0, -1, -1, + 2, "ldmia", "r!0d!!, <!1R>"), + ENCODING_MAP(ARMV5TE_LDR_RRI5, 0x6800, 2, 0, 5, 3, 10, 6, + 3, "ldr", "r!0d, [r!1d, #!2E]"), + ENCODING_MAP(ARMV5TE_LDR_RRR, 0x5800, 2, 0, 5, 3, 8, 6, + 3, "ldr", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_LDR_PC_REL, 0x4800, 10, 8, 7, 0, -1, -1, + 2, "ldr", "r!0d, [pc, #!1E]"), + ENCODING_MAP(ARMV5TE_LDR_SP_REL, 0x9800, 10, 8, 7, 0, -1, -1, + 2, "ldr", "r!0d, [sp, #!1E]"), + ENCODING_MAP(ARMV5TE_LDRB_RRI5, 0x7800, 2, 0, 5, 3, 10, 6, + 3, "ldrb", "r!0d, [r!1d, #2d]"), + ENCODING_MAP(ARMV5TE_LDRB_RRR, 0x5c00, 2, 0, 5, 3, 8, 6, + 3, "ldrb", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_LDRH_RRI5, 0x8800, 2, 0, 5, 3, 10, 6, + 3, "ldrh", "r!0d, [r!1d, #!2F]"), + ENCODING_MAP(ARMV5TE_LDRH_RRR, 0x5a00, 2, 0, 5, 3, 8, 6, + 3, "ldrh", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_LDRSB_RRR, 0x5600, 2, 0, 5, 3, 8, 6, + 3, "ldrsb", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_LDRSH_RRR, 0x5e00, 2, 0, 5, 3, 8, 6, + 3, "ldrsh", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_LSL, 0x0000, 2, 0, 5, 3, 10, 6, + 3, "lsl", "r!0d, r!1d, #!2d"), + ENCODING_MAP(ARMV5TE_LSLV, 0x4080, 2, 0, 5, 3, -1, -1, + 2, "lsl", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_LSR, 0x0800, 2, 0, 5, 3, 10, 6, + 3, "lsr", "r!0d, r!1d, #!2d"), + ENCODING_MAP(ARMV5TE_LSRV, 0x40c0, 2, 0, 5, 3, -1, -1, + 2, "lsr", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_MOV_IMM, 0x2000, 10, 8, 7, 0, -1, -1, + 2, "mov", "r!0d, #!1d"), + ENCODING_MAP(ARMV5TE_MOV_RR, 0x1c00, 2, 0, 5, 3, -1, -1, + 2, "mov", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_MOV_RR_LH, 0x4640, 2, 0, 5, 3, -1, -1, + 2, "mov", "r!0D, r!1d"), + ENCODING_MAP(ARMV5TE_MOV_RR_HL, 0x4680, 2, 0, 5, 3, -1, -1, + 2, "mov", "r!0d, r!1D"), + ENCODING_MAP(ARMV5TE_MOV_RR_HH, 0x46c0, 2, 0, 5, 3, -1, -1, + 2, "mov", "r!0D, r!1D"), + ENCODING_MAP(ARMV5TE_MUL, 0x4340, 2, 0, 5, 3, -1, -1, + 2, "mul", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_MVN, 0x43c0, 2, 0, 5, 3, -1, -1, + 2, "mvn", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_NEG, 0x4240, 2, 0, 5, 3, -1, -1, + 2, "neg", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_ORR, 0x4300, 2, 0, 5, 3, -1, -1, + 2, "orr", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_POP, 0xbc00, 8, 0, -1, -1, -1, -1, + 1, "pop", "<!0R>"), + ENCODING_MAP(ARMV5TE_PUSH, 0xb400, 8, 0, -1, -1, -1, -1, + 1, "push", "<!0R>"), + ENCODING_MAP(ARMV5TE_ROR, 0x41c0, 2, 0, 5, 3, -1, -1, + 2, "ror", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_SBC, 0x4180, 2, 0, 5, 3, -1, -1, + 2, "sbc", "r!0d, r!1d"), + ENCODING_MAP(ARMV5TE_STMIA, 0xc000, 10, 8, 7, 0, -1, -1, + 2, "stmia", "r!0d!!, <!1R>"), + ENCODING_MAP(ARMV5TE_STR_RRI5, 0x6000, 2, 0, 5, 3, 10, 6, + 3, "str", "r!0d, [r!1d, #!2E]"), + ENCODING_MAP(ARMV5TE_STR_RRR, 0x5000, 2, 0, 5, 3, 8, 6, + 3, "str", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_STR_SP_REL, 0x9000, 10, 8, 7, 0, -1, -1, + 2, "str", "r!0d, [sp, #!1E]"), + ENCODING_MAP(ARMV5TE_STRB_RRI5, 0x7000, 2, 0, 5, 3, 10, 6, + 3, "strb", "r!0d, [r!1d, #!2d]"), + ENCODING_MAP(ARMV5TE_STRB_RRR, 0x5400, 2, 0, 5, 3, 8, 6, + 3, "strb", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_STRH_RRI5, 0x8000, 2, 0, 5, 3, 10, 6, + 3, "strh", "r!0d, [r!1d, #!2F]"), + ENCODING_MAP(ARMV5TE_STRH_RRR, 0x5200, 2, 0, 5, 3, 8, 6, + 3, "strh", "r!0d, [r!1d, r!2d]"), + ENCODING_MAP(ARMV5TE_SUB_RRI3, 0x1e00, 2, 0, 5, 3, 8, 6, + 3, "sub", "r!0d, r!1d, #!2d]"), + ENCODING_MAP(ARMV5TE_SUB_RI8, 0x3800, 10, 8, 7, 0, -1, -1, + 2, "sub", "r!0d, #!1d"), + ENCODING_MAP(ARMV5TE_SUB_RRR, 0x1a00, 2, 0, 5, 3, 8, 6, + 3, "sub", "r!0d, r!1d, r!2d"), + ENCODING_MAP(ARMV5TE_SUB_SPI7, 0xb080, 6, 0, -1, -1, -1, -1, + 1, "sub", "sp, #!0d"), + ENCODING_MAP(ARMV5TE_SWI, 0xdf00, 7, 0, -1, -1, -1, -1, + 1, "swi", "!0d"), + ENCODING_MAP(ARMV5TE_TST, 0x4200, 2, 0, 5, 3, -1, -1, + 1, "tst", "r!0d, r!1d"), +}; + +#define PADDING_MOV_R0_R0 0x1C00 + +/* Write the numbers in the literal pool to the codegen stream */ +static void writeDataContent(CompilationUnit *cUnit) +{ + int *dataPtr = (int *) (cUnit->codeBuffer + cUnit->dataOffset); + Armv5teLIR *dataLIR = (Armv5teLIR *) cUnit->wordList; + while (dataLIR) { + *dataPtr++ = dataLIR->operands[0]; + dataLIR = NEXT_LIR(dataLIR); + } +} + +/* Return TRUE if error happens */ +static bool assembleInstructions(CompilationUnit *cUnit, intptr_t startAddr) +{ + short *bufferAddr = (short *) cUnit->codeBuffer; + Armv5teLIR *lir; + bool retry = false; + + for (lir = (Armv5teLIR *) cUnit->firstLIRInsn; lir; lir = NEXT_LIR(lir)) { + if (lir->opCode < 0) { + if ((lir->opCode == ARMV5TE_PSEUDO_ALIGN4) && + (lir->operands[0] == 1) && + !retry) { + *bufferAddr++ = PADDING_MOV_R0_R0; + } + continue; + } + + if (lir->opCode == ARMV5TE_LDR_PC_REL || + lir->opCode == ARMV5TE_ADD_PC_REL) { + Armv5teLIR *lirTarget = (Armv5teLIR *) lir->generic.target; + intptr_t pc = (lir->generic.offset + 4) & ~3; + intptr_t target = lirTarget->generic.offset; + int delta = target - pc; + if (delta & 0x3) { + LOGE("PC-rel distance is not multiples of 4: %d\n", delta); + dvmAbort(); + } + lir->operands[1] = delta >> 2; + } else if (lir->opCode == ARMV5TE_B_COND) { + Armv5teLIR *targetLIR = (Armv5teLIR *) lir->generic.target; + intptr_t pc = lir->generic.offset + 4; + intptr_t target = targetLIR->generic.offset; + int delta = target - pc; + if (delta > 254 || delta < -256) { + /* Pull in the PC reconstruction code inline */ + if (targetLIR->opCode == ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL){ + /* + * The original code is: + * + * bxx targetLIR + * origNextLir + * : + * : + * targetLIR (a PC reconstruction cell) + * : + * lastLIR (should be a unconditional branch) + * + * The distance from bxx to targetLIR is too far, so we want + * to rearrange the code to be: + * + * bxx targetLIR + * branchoverLIR to origNextLir + * targetLIR (a PC reconstruction cell) + * : + * lastLIR (should be a unconditional branch) + * origNextLir + * + * Although doing so adds a unconditional branchover + * instruction, it can be predicted for free by ARM so + * the penalty should be minimal. + */ + Armv5teLIR *pcrLIR = targetLIR; + Armv5teLIR *lastLIR = pcrLIR; + Armv5teLIR *origNextLIR = NEXT_LIR(lir); + + /* + * Find out the last instruction in the PC reconstruction + * cell + */ + while (lastLIR->opCode != ARMV5TE_B_UNCOND) { + lastLIR = NEXT_LIR(lastLIR); + } + + /* Yank out the PCR code */ + PREV_LIR_LVALUE(NEXT_LIR(lastLIR)) = + (LIR *) PREV_LIR(targetLIR); + NEXT_LIR_LVALUE(PREV_LIR(targetLIR)) = + (LIR *) NEXT_LIR(lastLIR); + + /* Create the branch over instruction */ + Armv5teLIR *branchoverLIR = + dvmCompilerNew(sizeof(Armv5teLIR), true); + branchoverLIR->opCode = ARMV5TE_B_UNCOND; + branchoverLIR->generic.target = (LIR *) origNextLIR; + + /* Reconnect the instructions */ + NEXT_LIR_LVALUE(lir) = (LIR *) branchoverLIR; + PREV_LIR_LVALUE(branchoverLIR) = (LIR *) lir; + + NEXT_LIR_LVALUE(branchoverLIR) = (LIR *) targetLIR; + PREV_LIR_LVALUE(targetLIR) = (LIR *) branchoverLIR; + + NEXT_LIR_LVALUE(lastLIR) = (LIR *) origNextLIR; + PREV_LIR_LVALUE(origNextLIR) = (LIR *) lastLIR; + + retry = true; + continue; + } else { + LOGE("Conditional branch distance out of range: %d\n", + delta); + dvmAbort(); + } + } + lir->operands[0] = delta >> 1; + } else if (lir->opCode == ARMV5TE_B_UNCOND) { + Armv5teLIR *targetLIR = (Armv5teLIR *) lir->generic.target; + intptr_t pc = lir->generic.offset + 4; + intptr_t target = targetLIR->generic.offset; + int delta = target - pc; + if (delta > 2046 || delta < -2048) { + LOGE("Unconditional branch distance out of range: %d\n", delta); + dvmAbort(); + } + lir->operands[0] = delta >> 1; + } else if (lir->opCode == ARMV5TE_BLX_1) { + assert(NEXT_LIR(lir)->opCode == ARMV5TE_BLX_2); + /* curPC is Thumb */ + intptr_t curPC = (startAddr + lir->generic.offset + 4) & ~3; + intptr_t target = lir->operands[1]; + + /* Match bit[1] in target with base */ + if (curPC & 0x2) { + target |= 0x2; + } + int delta = target - curPC; + assert((delta >= -(1<<22)) && (delta <= ((1<<22)-2))); + + lir->operands[0] = (delta >> 12) & 0x7ff; + NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff; + } + + /* + * The code offset will be recalculated, just continue to check if + * there are other places where code will be rescheduled and do not + * write to the output buffer + */ + if (retry) { + continue; + } + Armv5teEncodingMap *encoder = &EncodingMap[lir->opCode]; + short bits = encoder->skeleton; + int i; + for (i = 0; i < 3; i++) { + short value; + if (encoder->fieldLoc[i].end != -1) { + value = (lir->operands[i] << encoder->fieldLoc[i].start) & + ((1 << (encoder->fieldLoc[i].end + 1)) - 1); + bits |= value; + + } + } + *bufferAddr++ = bits; + } + return retry; +} + +/* + * Go over each instruction in the list and calculate the offset from the top + * before sending them off to the assembler. If out-of-range branch distance is + * seen rearrange the instructions a bit to correct it. + */ +void dvmCompilerAssembleLIR(CompilationUnit *cUnit) +{ + LIR *lir; + Armv5teLIR *armLIR; + int offset; + int i; + +retry: + for (armLIR = (Armv5teLIR *) cUnit->firstLIRInsn, offset = 0; + armLIR; + armLIR = NEXT_LIR(armLIR)) { + armLIR->generic.offset = offset; + if (armLIR->opCode >= 0) { + offset += 2; + } else if (armLIR->opCode == ARMV5TE_PSEUDO_ALIGN4) { + if (offset & 0x2) { + offset += 2; + armLIR->operands[0] = 1; + } else { + armLIR->operands[0] = 0; + } + } + /* Pseudo opcodes don't consume space */ + } + + /* Const values have to be word aligned */ + offset = ((offset + 3) >> 2) << 2; + + cUnit->dataOffset = offset; + + for (lir = cUnit->wordList; lir; lir = lir->next) { + lir->offset = offset; + offset += 4; + } + + cUnit->totalSize = offset; + + if (gDvmJit.codeCacheByteUsed + offset > CODE_CACHE_SIZE) { + gDvmJit.codeCacheFull = true; + cUnit->baseAddr = NULL; + return; + } + cUnit->codeBuffer = dvmCompilerNew(offset, true); + if (cUnit->codeBuffer == NULL) { + LOGE("Code buffer allocation failure\n"); + cUnit->baseAddr = NULL; + return; + } + + bool needRetry = assembleInstructions( + cUnit, (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed); + + if (needRetry) + goto retry; + + writeDataContent(cUnit); + + cUnit->baseAddr = (char *) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed; + gDvmJit.codeCacheByteUsed += offset; + + + /* Install the compilation */ + memcpy(cUnit->baseAddr, cUnit->codeBuffer, offset); + gDvmJit.numCompilations++; + + /* Flush dcache and invalidate the icache to maintain coherence */ + cacheflush((intptr_t) cUnit->baseAddr, + (intptr_t) (cUnit->baseAddr + offset), 0); +} + +/* + * Perform translation chain operation. + * For ARM, we'll use a pair of thumb instructions to generate + * an unconditional chaining branch of up to 4MB in distance. + * Use a BL, though we don't really need the link. The format is + * 111HHooooooooooo + * Where HH is 10 for the 1st inst, and 11 for the second and + * the "o" field is each instruction's 11-bit contribution to the + * 22-bit branch offset. + * TUNING: use a single-instruction variant if it reaches. + */ +void* dvmJitChain(void* tgtAddr, u4* branchAddr) +{ + int baseAddr = (u4) branchAddr + 4; + int branchOffset = (int) tgtAddr - baseAddr; + u4 thumb1; + u4 thumb2; + u4 newInst; + + assert((branchOffset >= -(1<<22)) && (branchOffset <= ((1<<22)-2))); + + gDvmJit.translationChains++; + + COMPILER_TRACE_CHAINING( + LOGD("Jit Runtime: chaining 0x%x to 0x%x\n", + (int) branchAddr, (int) tgtAddr & -2)); + if ((branchOffset < -2048) | (branchOffset > 2046)) { + thumb1 = (0xf000 | ((branchOffset>>12) & 0x7ff)); + thumb2 = (0xf800 | ((branchOffset>> 1) & 0x7ff)); + } else { + thumb1 = (0xe000 | ((branchOffset>> 1) & 0x7ff)); + thumb2 = 0x4300; /* nop -> or r0, r0 */ + } + + newInst = thumb2<<16 | thumb1; + *branchAddr = newInst; + cacheflush((intptr_t) branchAddr, (intptr_t) branchAddr + 4, 0); + + return tgtAddr; +} diff --git a/vm/compiler/codegen/armv5te/Codegen.c b/vm/compiler/codegen/armv5te/Codegen.c new file mode 100644 index 000000000..178e5368b --- /dev/null +++ b/vm/compiler/codegen/armv5te/Codegen.c @@ -0,0 +1,2892 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "Dalvik.h" +#include "interp/InterpDefs.h" +#include "libdex/OpCode.h" +#include "dexdump/OpCodeNames.h" +#include "vm/compiler/CompilerInternals.h" +#include "Armv5teLIR.h" +#include "vm/mterp/common/FindInterface.h" + +/* Create the TemplateOpcode enum */ +#define JIT_TEMPLATE(X) TEMPLATE_##X, +typedef enum { +#include "../../template/armv5te/TemplateOpList.h" +/* + * For example, + * TEMPLATE_CMP_LONG, + * TEMPLATE_RETURN, + * ... + */ + TEMPLATE_LAST_MARK, +} TemplateOpCode; +#undef JIT_TEMPLATE + +/* Array holding the entry offset of each template relative to the first one */ +static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK]; + +/* Track exercised opcodes */ +static int opcodeCoverage[256]; + +/*****************************************************************************/ + +/* + * The following are building blocks to construct low-level IRs with 0 - 3 + * operands. + */ +static Armv5teLIR *newLIR0(CompilationUnit *cUnit, Armv5teOpCode opCode) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 0); + insn->opCode = opCode; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR1(CompilationUnit *cUnit, Armv5teOpCode opCode, + int dest) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 1); + insn->opCode = opCode; + insn->operands[0] = dest; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR2(CompilationUnit *cUnit, Armv5teOpCode opCode, + int dest, int src1) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 2); + insn->opCode = opCode; + insn->operands[0] = dest; + insn->operands[1] = src1; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR3(CompilationUnit *cUnit, Armv5teOpCode opCode, + int dest, int src1, int src2) +{ + Armv5teLIR *insn = dvmCompilerNew(sizeof(Armv5teLIR), true); + assert(isPseudoOpCode(opCode) || EncodingMap[opCode].operands == 3); + insn->opCode = opCode; + insn->operands[0] = dest; + insn->operands[1] = src1; + insn->operands[2] = src2; + dvmCompilerAppendLIR(cUnit, (LIR *) insn); + return insn; +} + +static Armv5teLIR *newLIR23(CompilationUnit *cUnit, Armv5teOpCode opCode, + int srcdest, int src2) +{ + assert(!isPseudoOpCode(opCode)); + if (EncodingMap[opCode].operands==2) + return newLIR2(cUnit, opCode, srcdest, src2); + else + return newLIR3(cUnit, opCode, srcdest, srcdest, src2); +} + +/*****************************************************************************/ + +/* + * The following are building blocks to insert constants into the pool or + * instruction streams. + */ + +/* Add a 32-bit constant either in the constant pool or mixed with code */ +static Armv5teLIR *addWordData(CompilationUnit *cUnit, int value, bool inPlace) +{ + /* Add the constant to the literal pool */ + if (!inPlace) { + Armv5teLIR *newValue = dvmCompilerNew(sizeof(Armv5teLIR), true); + newValue->operands[0] = value; + newValue->generic.next = cUnit->wordList; + cUnit->wordList = (LIR *) newValue; + return newValue; + } else { + /* Add the constant in the middle of code stream */ + newLIR1(cUnit, ARMV5TE_16BIT_DATA, (value & 0xffff)); + newLIR1(cUnit, ARMV5TE_16BIT_DATA, (value >> 16)); + } + return NULL; +} + +/* + * Search the existing constants in the literal pool for an exact or close match + * within specified delta (greater or equal to 0). + */ +static Armv5teLIR *scanLiteralPool(CompilationUnit *cUnit, int value, + unsigned int delta) +{ + LIR *dataTarget = cUnit->wordList; + while (dataTarget) { + if (((unsigned) (value - ((Armv5teLIR *) dataTarget)->operands[0])) <= + delta) + return (Armv5teLIR *) dataTarget; + dataTarget = dataTarget->next; + } + return NULL; +} + +/* + * Load a immediate using a shortcut if possible; otherwise + * grab from the per-translation literal pool + */ +void loadConstant(CompilationUnit *cUnit, int rDest, int value) +{ + /* See if the value can be constructed cheaply */ + if ((value >= 0) && (value <= 255)) { + newLIR2(cUnit, ARMV5TE_MOV_IMM, rDest, value); + return; + } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) { + newLIR2(cUnit, ARMV5TE_MOV_IMM, rDest, ~value); + newLIR2(cUnit, ARMV5TE_MVN, rDest, rDest); + return; + } + /* No shortcut - go ahead and use literal pool */ + Armv5teLIR *dataTarget = scanLiteralPool(cUnit, value, 255); + if (dataTarget == NULL) { + dataTarget = addWordData(cUnit, value, false); + } + Armv5teLIR *loadPcRel = dvmCompilerNew(sizeof(Armv5teLIR), true); + loadPcRel->opCode = ARMV5TE_LDR_PC_REL; + loadPcRel->generic.target = (LIR *) dataTarget; + loadPcRel->operands[0] = rDest; + dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel); + + /* + * To save space in the constant pool, we use the ADD_RRI8 instruction to + * add up to 255 to an existing constant value. + */ + if (dataTarget->operands[0] != value) { + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, value - dataTarget->operands[0]); + } +} + +/* Export the Dalvik PC assicated with an instruction to the StackSave area */ +static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr) +{ + int offset = offsetof(StackSaveArea, xtra.currentPc); + loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset)); + newLIR2(cUnit, ARMV5TE_MOV_RR, rAddr, rFP); + newLIR2(cUnit, ARMV5TE_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset); + newLIR3(cUnit, ARMV5TE_STR_RRI5, rDPC, rAddr, 0); +} + +/* Generate conditional branch instructions */ +static void genConditionalBranch(CompilationUnit *cUnit, + Armv5teConditionCode cond, + Armv5teLIR *target) +{ + Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond); + branch->generic.target = (LIR *) target; +} + +/* Generate unconditional branch instructions */ +static void genUnconditionalBranch(CompilationUnit *cUnit, Armv5teLIR *target) +{ + Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND); + branch->generic.target = (LIR *) target; +} + +#define USE_IN_CACHE_HANDLER 1 + +/* + * Jump to the out-of-line handler in ARM mode to finish executing the + * remaining of more complex instructions. + */ +static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpCode opCode) +{ +#if USE_IN_CACHE_HANDLER + /* + * NOTE - In practice BLX only needs one operand, but since the assembler + * may abort itself and retry due to other out-of-range conditions we + * cannot really use operand[0] to store the absolute target address since + * it may get clobbered by the final relative offset. Therefore, + * we fake BLX_1 is a two operand instruction and the absolute target + * address is stored in operand[1]. + */ + newLIR2(cUnit, ARMV5TE_BLX_1, + (int) gDvmJit.codeCache + templateEntryOffsets[opCode], + (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); + newLIR2(cUnit, ARMV5TE_BLX_2, + (int) gDvmJit.codeCache + templateEntryOffsets[opCode], + (int) gDvmJit.codeCache + templateEntryOffsets[opCode]); +#else + /* + * In case we want to access the statically compiled handlers for + * debugging purposes, define USE_IN_CACHE_HANDLER to 0 + */ + void *templatePtr; + +#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X(); +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + switch (opCode) { +#define JIT_TEMPLATE(X) \ + case TEMPLATE_##X: { templatePtr = dvmCompiler_TEMPLATE_##X; break; } +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + default: templatePtr = NULL; + } + loadConstant(cUnit, r7, (int) templatePtr); + newLIR1(cUnit, ARMV5TE_BLX_R, r7); +#endif +} + +/* Perform the actual operation for OP_RETURN_* */ +static void genReturnCommon(CompilationUnit *cUnit, MIR *mir) +{ + genDispatchToHandler(cUnit, TEMPLATE_RETURN); +#if defined(INVOKE_STATS) + gDvmJit.jitReturn++; +#endif + int dPC = (int) (cUnit->method->insns + mir->offset); + Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND); + /* Set up the place holder to reconstruct this Dalvik PC */ + Armv5teLIR *pcrLabel = dvmCompilerNew(sizeof(Armv5teLIR), true); + pcrLabel->opCode = ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL; + pcrLabel->operands[0] = dPC; + pcrLabel->operands[1] = mir->offset; + /* Insert the place holder to the growable list */ + dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel); + /* Branch to the PC reconstruction code */ + branch->generic.target = (LIR *) pcrLabel; +} + +/* + * Load a pair of values of rFP[src..src+1] and store them into rDestLo and + * rDestHi + */ +static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo, + int rDestHi) +{ + /* Use reg + imm5*4 to load the values if possible */ + if (vSrc <= 30) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDestLo, rFP, vSrc); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDestHi, rFP, vSrc+1); + } else { + if (vSrc <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDestLo, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDestHi, (vSrc-1)*4); + } else { + /* Offset too far from rFP */ + loadConstant(cUnit, rDestLo, vSrc*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rDestLo, rFP, rDestLo); + } + assert(rDestLo != rDestHi); + newLIR2(cUnit, ARMV5TE_LDMIA, rDestLo, (1<<rDestLo) | (1<<(rDestHi))); + } +} + +/* + * Store a pair of values of rSrc and rSrc+1 and store them into vDest and + * vDest+1 + */ +static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi, + int vDest, int rScratch) +{ + /* Use reg + imm5*4 to store the values if possible */ + if (vDest <= 30) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrcLo, rFP, vDest); + newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrcHi, rFP, vDest+1); + } else { + if (vDest <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rScratch, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rScratch, (vDest-1)*4); + } else { + /* Offset too far from rFP */ + loadConstant(cUnit, rScratch, vDest*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rScratch, rFP, rScratch); + } + assert(rSrcLo != rSrcHi); + newLIR2(cUnit, ARMV5TE_STMIA, rScratch, (1<<rSrcLo) | (1 << (rSrcHi))); + } +} + +/* Load the address of a Dalvik register on the frame */ +static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest) +{ + /* RRI3 can add up to 7 */ + if (vSrc <= 1) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, vSrc*4); + } else if (vSrc <= 64) { + /* Sneak 4 into the base address first */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, (vSrc-1)*4); + } else { + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rDest, rFP, rDest); + } +} + + +/* Load a single value from rFP[src] and store them into rDest */ +static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest) +{ + /* Use reg + imm5*4 to load the value if possible */ + if (vSrc <= 31) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, rDest, rFP, vSrc); + } else { + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, ARMV5TE_LDR_RRR, rDest, rFP, rDest); + } +} + +/* Store a value from rSrc to vDest */ +static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest, + int rScratch) +{ + /* Use reg + imm5*4 to store the value if possible */ + if (vDest <= 31) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, rSrc, rFP, vDest); + } else { + loadConstant(cUnit, rScratch, vDest*4); + newLIR3(cUnit, ARMV5TE_STR_RRR, rSrc, rFP, rScratch); + } +} + +/* Calculate the address of rFP+vSrc*4 */ +static void calculateValueAddress(CompilationUnit *cUnit, int vSrc, int rDest) +{ + /* Use add rd, rs, imm_3 */ + if (vSrc <= 1) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, vSrc*4); + } else if (vSrc <= 64) { + /* Use add rd, imm_8 */ + /* Sneak in 4 above rFP to cover one more register offset (ie v64) */ + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rDest, rFP, 4); + newLIR2(cUnit, ARMV5TE_ADD_RI8, rDest, (vSrc-1)*4); + } else { + /* Load offset from the constant pool */ + loadConstant(cUnit, rDest, vSrc*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, rDest, rFP, rDest); + } +} + +/* + * Perform a binary operation on 64-bit operands and leave the results in the + * r0/r1 pair. + */ +static void genBinaryOpWide(CompilationUnit *cUnit, int vDest, + Armv5teOpCode preinst, Armv5teOpCode inst) +{ + newLIR23(cUnit, preinst, r0, r2); + newLIR23(cUnit, inst, r1, r3); + storeValuePair(cUnit, r0, r1, vDest, r2); +} + +/* Perform a binary operation on 32-bit operands and leave the results in r0. */ +static void genBinaryOp(CompilationUnit *cUnit, int vDest, Armv5teOpCode inst) +{ + newLIR23(cUnit, inst, r0, r1); + storeValue(cUnit, r0, vDest, r1); +} + +/* Create the PC reconstruction slot if not already done */ +static inline Armv5teLIR *genCheckCommon(CompilationUnit *cUnit, int dOffset, + Armv5teLIR *branch, + Armv5teLIR *pcrLabel) +{ + /* Set up the place holder to reconstruct this Dalvik PC */ + if (pcrLabel == NULL) { + int dPC = (int) (cUnit->method->insns + dOffset); + pcrLabel = dvmCompilerNew(sizeof(Armv5teLIR), true); + pcrLabel->opCode = ARMV5TE_PSEUDO_PC_RECONSTRUCTION_CELL; + pcrLabel->operands[0] = dPC; + pcrLabel->operands[1] = dOffset; + /* Insert the place holder to the growable list */ + dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel); + } + /* Branch to the PC reconstruction code */ + branch->generic.target = (LIR *) pcrLabel; + return pcrLabel; +} + +/* + * Perform a "reg cmp imm" operation and jump to the PCR region if condition + * satisfies. + */ +static inline Armv5teLIR *genRegImmCheck(CompilationUnit *cUnit, + Armv5teConditionCode cond, int reg, + int checkValue, int dOffset, + Armv5teLIR *pcrLabel) +{ + newLIR2(cUnit, ARMV5TE_CMP_RI8, reg, checkValue); + Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond); + return genCheckCommon(cUnit, dOffset, branch, pcrLabel); +} + +/* + * Perform a "reg cmp reg" operation and jump to the PCR region if condition + * satisfies. + */ +static inline Armv5teLIR *inertRegRegCheck(CompilationUnit *cUnit, + Armv5teConditionCode cond, + int reg1, int reg2, int dOffset, + Armv5teLIR *pcrLabel) +{ + newLIR2(cUnit, ARMV5TE_CMP_RR, reg1, reg2); + Armv5teLIR *branch = newLIR2(cUnit, ARMV5TE_B_COND, 0, cond); + return genCheckCommon(cUnit, dOffset, branch, pcrLabel); +} + +/* Perform null-check on a register */ +static Armv5teLIR *genNullCheck(CompilationUnit *cUnit, int reg, int dOffset, + Armv5teLIR *pcrLabel) +{ + return genRegImmCheck(cUnit, ARM_COND_EQ, reg, 0, dOffset, pcrLabel); +} + +/* Perform bound check on two registers */ +static Armv5teLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex, + int rBound, int dOffset, Armv5teLIR *pcrLabel) +{ + return inertRegRegCheck(cUnit, ARM_COND_CS, rIndex, rBound, dOffset, + pcrLabel); +} + +/* Generate a unconditional branch to go to the interpreter */ +static inline Armv5teLIR *genTrap(CompilationUnit *cUnit, int dOffset, + Armv5teLIR *pcrLabel) +{ + Armv5teLIR *branch = newLIR0(cUnit, ARMV5TE_B_UNCOND); + return genCheckCommon(cUnit, dOffset, branch, pcrLabel); +} + +/* Load a wide field from an object instance */ +static void genIGetWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + loadValue(cUnit, dInsn->vB, r2); + loadConstant(cUnit, r3, fieldOffset); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, r2, r3); + newLIR2(cUnit, ARMV5TE_LDMIA, r2, (1<<r0 | 1<<r1)); + storeValuePair(cUnit, r0, r1, dInsn->vA, r3); +} + +/* Store a wide field to an object instance */ +static void genIPutWide(CompilationUnit *cUnit, MIR *mir, int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + loadValue(cUnit, dInsn->vB, r2); + loadValuePair(cUnit, dInsn->vA, r0, r1); + loadConstant(cUnit, r3, fieldOffset); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, r2, r3); + newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1<<r1)); +} + +/* + * Load a field from an object instance + * + * Inst should be one of: + * ARMV5TE_LDR_RRR + * ARMV5TE_LDRB_RRR + * ARMV5TE_LDRH_RRR + * ARMV5TE_LDRSB_RRR + * ARMV5TE_LDRSH_RRR + */ +static void genIGet(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + /* TUNING: write a utility routine to load via base + constant offset */ + loadValue(cUnit, dInsn->vB, r0); + loadConstant(cUnit, r1, fieldOffset); + genNullCheck(cUnit, r0, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, inst, r0, r0, r1); + storeValue(cUnit, r0, dInsn->vA, r1); +} + +/* + * Store a field to an object instance + * + * Inst should be one of: + * ARMV5TE_STR_RRR + * ARMV5TE_STRB_RRR + * ARMV5TE_STRH_RRR + */ +static void genIPut(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int fieldOffset) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + + /* TUNING: write a utility routine to load via base + constant offset */ + loadValue(cUnit, dInsn->vB, r2); + loadConstant(cUnit, r1, fieldOffset); + loadValue(cUnit, dInsn->vA, r0); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, inst, r0, r2, r1); +} + + +/* TODO: This should probably be done as an out-of-line instruction handler. */ + +/* + * Generate array load + * + * Inst should be one of: + * ARMV5TE_LDR_RRR + * ARMV5TE_LDRB_RRR + * ARMV5TE_LDRH_RRR + * ARMV5TE_LDRSB_RRR + * ARMV5TE_LDRSH_RRR + */ +static void genArrayGet(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int vArray, int vIndex, int vDest, int scale) +{ + int lenOffset = offsetof(ArrayObject, length); + int dataOffset = offsetof(ArrayObject, contents); + + loadValue(cUnit, vArray, r2); + loadValue(cUnit, vIndex, r3); + + /* null object? */ + Armv5teLIR * pcrLabel = genNullCheck(cUnit, r2, mir->offset, NULL); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r2, lenOffset >> 2); /* Get len */ + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, dataOffset); /* r2 -> array data */ + genBoundsCheck(cUnit, r3, r0, mir->offset, pcrLabel); + if (scale) { + newLIR3(cUnit, ARMV5TE_LSL, r3, r3, scale); + } + if (scale==3) { + newLIR3(cUnit, inst, r0, r2, r3); + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, 4); + newLIR3(cUnit, inst, r1, r2, r3); + storeValuePair(cUnit, r0, r1, vDest, r3); + } else { + newLIR3(cUnit, inst, r0, r2, r3); + storeValue(cUnit, r0, vDest, r3); + } +} + +/* TODO: This should probably be done as an out-of-line instruction handler. */ + +/* + * Generate array store + * + * Inst should be one of: + * ARMV5TE_STR_RRR + * ARMV5TE_STRB_RRR + * ARMV5TE_STRH_RRR + */ +static void genArrayPut(CompilationUnit *cUnit, MIR *mir, Armv5teOpCode inst, + int vArray, int vIndex, int vSrc, int scale) +{ + int lenOffset = offsetof(ArrayObject, length); + int dataOffset = offsetof(ArrayObject, contents); + + loadValue(cUnit, vArray, r2); + loadValue(cUnit, vIndex, r3); + genNullCheck(cUnit, r2, mir->offset, NULL); /* null object? */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r2, lenOffset >> 2); /* Get len */ + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, dataOffset); /* r2 -> array data */ + genBoundsCheck(cUnit, r3, r0, mir->offset, NULL); + /* at this point, r2 points to array, r3 is unscaled index */ + if (scale==3) { + loadValuePair(cUnit, vSrc, r0, r1); + } else { + loadValue(cUnit, vSrc, r0); + } + if (scale) { + newLIR3(cUnit, ARMV5TE_LSL, r3, r3, scale); + } + /* + * at this point, r2 points to array, r3 is scaled index, and r0[r1] is + * data + */ + if (scale==3) { + newLIR3(cUnit, inst, r0, r2, r3); + newLIR2(cUnit, ARMV5TE_ADD_RI8, r2, 4); + newLIR3(cUnit, inst, r1, r2, r3); + } else { + newLIR3(cUnit, inst, r0, r2, r3); + } +} + +static bool genShiftOpLong(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vShift) +{ + loadValuePair(cUnit, vSrc1, r0, r1); + loadValue(cUnit, vShift, r2); + switch( mir->dalvikInsn.opCode) { + case OP_SHL_LONG: + case OP_SHL_LONG_2ADDR: + genDispatchToHandler(cUnit, TEMPLATE_SHL_LONG); + break; + case OP_SHR_LONG: + case OP_SHR_LONG_2ADDR: + genDispatchToHandler(cUnit, TEMPLATE_SHR_LONG); + break; + case OP_USHR_LONG: + case OP_USHR_LONG_2ADDR: + genDispatchToHandler(cUnit, TEMPLATE_USHR_LONG); + break; + default: + return true; + } + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; +} + +static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + void* funct; + /* TODO: use a proper include file to define these */ + float __aeabi_fadd(float a, float b); + float __aeabi_fsub(float a, float b); + float __aeabi_fdiv(float a, float b); + float __aeabi_fmul(float a, float b); + float fmodf(float a, float b); + + switch (mir->dalvikInsn.opCode) { + case OP_ADD_FLOAT_2ADDR: + case OP_ADD_FLOAT: + funct = (void*) __aeabi_fadd; + break; + case OP_SUB_FLOAT_2ADDR: + case OP_SUB_FLOAT: + funct = (void*) __aeabi_fsub; + break; + case OP_DIV_FLOAT_2ADDR: + case OP_DIV_FLOAT: + funct = (void*) __aeabi_fdiv; + break; + case OP_MUL_FLOAT_2ADDR: + case OP_MUL_FLOAT: + funct = (void*) __aeabi_fmul; + break; + case OP_REM_FLOAT_2ADDR: + case OP_REM_FLOAT: + funct = (void*) fmodf; + break; + case OP_NEG_FLOAT: { + loadValue(cUnit, vSrc2, r0); + loadConstant(cUnit, r1, 0x80000000); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r0, r0, r1); + storeValue(cUnit, r0, vDest, r1); + return false; + } + default: + return true; + } + loadConstant(cUnit, r2, (int)funct); + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, r0, vDest, r1); + return false; +} + +static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + void* funct; + /* TODO: use a proper include file to define these */ + double __aeabi_dadd(double a, double b); + double __aeabi_dsub(double a, double b); + double __aeabi_ddiv(double a, double b); + double __aeabi_dmul(double a, double b); + double fmod(double a, double b); + + switch (mir->dalvikInsn.opCode) { + case OP_ADD_DOUBLE_2ADDR: + case OP_ADD_DOUBLE: + funct = (void*) __aeabi_dadd; + break; + case OP_SUB_DOUBLE_2ADDR: + case OP_SUB_DOUBLE: + funct = (void*) __aeabi_dsub; + break; + case OP_DIV_DOUBLE_2ADDR: + case OP_DIV_DOUBLE: + funct = (void*) __aeabi_ddiv; + break; + case OP_MUL_DOUBLE_2ADDR: + case OP_MUL_DOUBLE: + funct = (void*) __aeabi_dmul; + break; + case OP_REM_DOUBLE_2ADDR: + case OP_REM_DOUBLE: + funct = (void*) fmod; + break; + case OP_NEG_DOUBLE: { + loadValuePair(cUnit, vSrc2, r0, r1); + loadConstant(cUnit, r2, 0x80000000); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r1, r1, r2); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; + } + default: + return true; + } + loadConstant(cUnit, r4PC, (int)funct); + loadValuePair(cUnit, vSrc1, r0, r1); + loadValuePair(cUnit, vSrc2, r2, r3); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; +} + +static bool genArithOpLong(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + int firstOp = ARMV5TE_BKPT; + int secondOp = ARMV5TE_BKPT; + bool callOut = false; + void *callTgt; + int retReg = r0; + /* TODO - find proper .h file to declare these */ + long long __aeabi_ldivmod(long long op1, long long op2); + + switch (mir->dalvikInsn.opCode) { + case OP_NOT_LONG: + firstOp = ARMV5TE_MVN; + secondOp = ARMV5TE_MVN; + break; + case OP_ADD_LONG: + case OP_ADD_LONG_2ADDR: + firstOp = ARMV5TE_ADD_RRR; + secondOp = ARMV5TE_ADC; + break; + case OP_SUB_LONG: + case OP_SUB_LONG_2ADDR: + firstOp = ARMV5TE_SUB_RRR; + secondOp = ARMV5TE_SBC; + break; + case OP_MUL_LONG: + case OP_MUL_LONG_2ADDR: + loadValuePair(cUnit, vSrc1, r0, r1); + loadValuePair(cUnit, vSrc2, r2, r3); + genDispatchToHandler(cUnit, TEMPLATE_MUL_LONG); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; + break; + case OP_DIV_LONG: + case OP_DIV_LONG_2ADDR: + callOut = true; + retReg = r0; + callTgt = (void*)__aeabi_ldivmod; + break; + /* NOTE - result is in r2/r3 instead of r0/r1 */ + case OP_REM_LONG: + case OP_REM_LONG_2ADDR: + callOut = true; + callTgt = (void*)__aeabi_ldivmod; + retReg = r2; + break; + case OP_AND_LONG: + case OP_AND_LONG_2ADDR: + firstOp = ARMV5TE_AND_RR; + secondOp = ARMV5TE_AND_RR; + break; + case OP_OR_LONG: + case OP_OR_LONG_2ADDR: + firstOp = ARMV5TE_ORR; + secondOp = ARMV5TE_ORR; + break; + case OP_XOR_LONG: + case OP_XOR_LONG_2ADDR: + firstOp = ARMV5TE_EOR; + secondOp = ARMV5TE_EOR; + break; + case OP_NEG_LONG: + loadValuePair(cUnit, vSrc2, r2, r3); + loadConstant(cUnit, r1, 0); + newLIR3(cUnit, ARMV5TE_SUB_RRR, r0, r1, r2); + newLIR2(cUnit, ARMV5TE_SBC, r1, r3); + storeValuePair(cUnit, r0, r1, vDest, r2); + return false; + default: + LOGE("Invalid long arith op"); + dvmAbort(); + } + if (!callOut) { + loadValuePair(cUnit, vSrc1, r0, r1); + loadValuePair(cUnit, vSrc2, r2, r3); + genBinaryOpWide(cUnit, vDest, firstOp, secondOp); + } else { + loadValuePair(cUnit, vSrc2, r2, r3); + loadConstant(cUnit, r4PC, (int) callTgt); + loadValuePair(cUnit, vSrc1, r0, r1); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + storeValuePair(cUnit, retReg, retReg+1, vDest, r4PC); + } + return false; +} + +static bool genArithOpInt(CompilationUnit *cUnit, MIR *mir, int vDest, + int vSrc1, int vSrc2) +{ + int armOp = ARMV5TE_BKPT; + bool callOut = false; + bool checkZero = false; + int retReg = r0; + void *callTgt; + + /* TODO - find proper .h file to declare these */ + int __aeabi_idivmod(int op1, int op2); + int __aeabi_idiv(int op1, int op2); + + switch (mir->dalvikInsn.opCode) { + case OP_NEG_INT: + armOp = ARMV5TE_NEG; + break; + case OP_NOT_INT: + armOp = ARMV5TE_MVN; + break; + case OP_ADD_INT: + case OP_ADD_INT_2ADDR: + armOp = ARMV5TE_ADD_RRR; + break; + case OP_SUB_INT: + case OP_SUB_INT_2ADDR: + armOp = ARMV5TE_SUB_RRR; + break; + case OP_MUL_INT: + case OP_MUL_INT_2ADDR: + armOp = ARMV5TE_MUL; + break; + case OP_DIV_INT: + case OP_DIV_INT_2ADDR: + callOut = true; + checkZero = true; + callTgt = __aeabi_idiv; + retReg = r0; + break; + /* NOTE: returns in r1 */ + case OP_REM_INT: + case OP_REM_INT_2ADDR: + callOut = true; + checkZero = true; + callTgt = __aeabi_idivmod; + retReg = r1; + break; + case OP_AND_INT: + case OP_AND_INT_2ADDR: + armOp = ARMV5TE_AND_RR; + break; + case OP_OR_INT: + case OP_OR_INT_2ADDR: + armOp = ARMV5TE_ORR; + break; + case OP_XOR_INT: + case OP_XOR_INT_2ADDR: + armOp = ARMV5TE_EOR; + break; + case OP_SHL_INT: + case OP_SHL_INT_2ADDR: + armOp = ARMV5TE_LSLV; + break; + case OP_SHR_INT: + case OP_SHR_INT_2ADDR: + armOp = ARMV5TE_ASRV; + break; + case OP_USHR_INT: + case OP_USHR_INT_2ADDR: + armOp = ARMV5TE_LSRV; + break; + default: + LOGE("Invalid word arith op: 0x%x(%d)", + mir->dalvikInsn.opCode, mir->dalvikInsn.opCode); + dvmAbort(); + } + if (!callOut) { + loadValue(cUnit, vSrc1, r0); + loadValue(cUnit, vSrc2, r1); + genBinaryOp(cUnit, vDest, armOp); + } else { + loadValue(cUnit, vSrc2, r1); + loadConstant(cUnit, r2, (int) callTgt); + loadValue(cUnit, vSrc1, r0); + if (checkZero) { + genNullCheck(cUnit, r1, mir->offset, NULL); + } + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, retReg, vDest, r2); + } + return false; +} + +static bool genArithOp(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vA = mir->dalvikInsn.vA; + int vB = mir->dalvikInsn.vB; + int vC = mir->dalvikInsn.vC; + + if ((opCode >= OP_ADD_LONG_2ADDR) && (opCode <= OP_XOR_LONG_2ADDR)) { + return genArithOpLong(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_LONG) && (opCode <= OP_XOR_LONG)) { + return genArithOpLong(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_SHL_LONG_2ADDR) && (opCode <= OP_USHR_LONG_2ADDR)) { + return genShiftOpLong(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_SHL_LONG) && (opCode <= OP_USHR_LONG)) { + return genShiftOpLong(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_USHR_INT_2ADDR)) { + return genArithOpInt(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_INT) && (opCode <= OP_USHR_INT)) { + return genArithOpInt(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_ADD_FLOAT_2ADDR) && (opCode <= OP_REM_FLOAT_2ADDR)) { + return genArithOpFloat(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_FLOAT) && (opCode <= OP_REM_FLOAT)) { + return genArithOpFloat(cUnit,mir, vA, vB, vC); + } + if ((opCode >= OP_ADD_DOUBLE_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) { + return genArithOpDouble(cUnit,mir, vA, vA, vB); + } + if ((opCode >= OP_ADD_DOUBLE) && (opCode <= OP_REM_DOUBLE)) { + return genArithOpDouble(cUnit,mir, vA, vB, vC); + } + return true; +} + +static bool genConversion(CompilationUnit *cUnit, MIR *mir, void *funct, + int srcSize, int tgtSize) +{ + loadConstant(cUnit, r2, (int)funct); + if (srcSize == 1) { + loadValue(cUnit, mir->dalvikInsn.vB, r0); + } else { + loadValuePair(cUnit, mir->dalvikInsn.vB, r0, r1); + } + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + if (tgtSize == 1) { + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + } else { + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + } + return false; +} + +/* Experimental example of completely inlining a native replacement */ +static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir) +{ + int offset = (int) &((InterpState *) NULL)->retval; + DecodedInstruction *dInsn = &mir->dalvikInsn; + assert(dInsn->vA == 1); + loadValue(cUnit, dInsn->arg[0], r0); + loadConstant(cUnit, r1, gDvm.offJavaLangString_count); + genNullCheck(cUnit, r0, mir->offset, NULL); + newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r1); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2); + return false; +} + +static void genProcessArgsNoRange(CompilationUnit *cUnit, MIR *mir, + DecodedInstruction *dInsn, + Armv5teLIR **pcrLabel) +{ + unsigned int i; + unsigned int regMask = 0; + + /* Load arguments to r0..r4 */ + for (i = 0; i < dInsn->vA; i++) { + regMask |= 1 << i; + loadValue(cUnit, dInsn->arg[i], i); + } + if (regMask) { + /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ + newLIR2(cUnit, ARMV5TE_MOV_RR, r7, rFP); + newLIR2(cUnit, ARMV5TE_SUB_RI8, r7, + sizeof(StackSaveArea) + (dInsn->vA << 2)); + /* generate null check */ + if (pcrLabel) { + *pcrLabel = genNullCheck(cUnit, r0, mir->offset, NULL); + } + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + } +} + +static void genProcessArgsRange(CompilationUnit *cUnit, MIR *mir, + DecodedInstruction *dInsn, + Armv5teLIR **pcrLabel) +{ + int srcOffset = dInsn->vC << 2; + int numArgs = dInsn->vA; + int regMask; + /* + * r4PC : &rFP[vC] + * r7: &newFP[0] + */ + if (srcOffset < 8) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, r4PC, rFP, srcOffset); + } else { + loadConstant(cUnit, r4PC, srcOffset); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r4PC, rFP, r4PC); + } + /* load [r0 .. min(numArgs,4)] */ + regMask = (1 << ((numArgs < 4) ? numArgs : 4)) - 1; + newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask); + + if (sizeof(StackSaveArea) + (numArgs << 2) < 256) { + newLIR2(cUnit, ARMV5TE_MOV_RR, r7, rFP); + newLIR2(cUnit, ARMV5TE_SUB_RI8, r7, + sizeof(StackSaveArea) + (numArgs << 2)); + } else { + loadConstant(cUnit, r7, sizeof(StackSaveArea) + (numArgs << 2)); + newLIR3(cUnit, ARMV5TE_SUB_RRR, r7, rFP, r7); + } + + /* generate null check */ + if (pcrLabel) { + *pcrLabel = genNullCheck(cUnit, r0, mir->offset, NULL); + } + + /* + * Handle remaining 4n arguments: + * store previously loaded 4 values and load the next 4 values + */ + if (numArgs >= 8) { + Armv5teLIR *loopLabel = NULL; + /* + * r0 contains "this" and it will be used later, so push it to the stack + * first. Pushing r5 is just for stack alignment purposes. + */ + newLIR1(cUnit, ARMV5TE_PUSH, 1 << r0 | 1 << 5); + /* No need to generate the loop structure if numArgs <= 11 */ + if (numArgs > 11) { + loadConstant(cUnit, 5, ((numArgs - 4) >> 2) << 2); + loopLabel = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL); + } + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask); + /* No need to generate the loop structure if numArgs <= 11 */ + if (numArgs > 11) { + newLIR2(cUnit, ARMV5TE_SUB_RI8, 5, 4); + genConditionalBranch(cUnit, ARM_COND_NE, loopLabel); + } + } + + /* Save the last batch of loaded values */ + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + + /* Generate the loop epilogue - don't use r0 */ + if ((numArgs > 4) && (numArgs % 4)) { + regMask = ((1 << (numArgs & 0x3)) - 1) << 1; + newLIR2(cUnit, ARMV5TE_LDMIA, r4PC, regMask); + } + if (numArgs >= 8) + newLIR1(cUnit, ARMV5TE_POP, 1 << r0 | 1 << 5); + + /* Save the modulo 4 arguments */ + if ((numArgs > 4) && (numArgs % 4)) { + newLIR2(cUnit, ARMV5TE_STMIA, r7, regMask); + } +} + +static void genInvokeCommon(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList, Armv5teLIR *pcrLabel, + const Method *calleeMethod) +{ + Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id]; + + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + addrRetChain->generic.target = (LIR *) retChainingCell; + /* + * r0 = calleeMethod (loaded upon calling genInvokeCommon) + * r1 = &ChainingCell + * r4PC = callsiteDPC + */ + if (dvmIsNativeMethod(calleeMethod)) { + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + } else { + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_CHAIN); +#if defined(INVOKE_STATS) + gDvmJit.invokeChain++; +#endif + genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); + } + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); +} + +/* Geneate a branch to go back to the interpreter */ +static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset) +{ + /* r0 = dalvik pc */ + loadConstant(cUnit, r0, (int) (cUnit->method->insns + offset)); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpPunt) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r1); +} + +/* + * Attempt to single step one instruction using the interpreter and return + * to the compiled code for the next Dalvik instruction + */ +static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir) +{ + int flags = dexGetInstrFlags(gDvm.instrFlags, mir->dalvikInsn.opCode); + int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn | + kInstrCanThrow; + if ((mir->next == NULL) || (flags & flagsToCheck)) { + genPuntToInterp(cUnit, mir->offset); + return; + } + int entryAddr = offsetof(InterpState, + jitToInterpEntries.dvmJitToInterpSingleStep); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r2, rGLUE, entryAddr >> 2); + /* r0 = dalvik pc */ + loadConstant(cUnit, r0, (int) (cUnit->method->insns + mir->offset)); + /* r1 = dalvik pc of following instruction */ + loadConstant(cUnit, r1, (int) (cUnit->method->insns + mir->next->offset)); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); +} + + +/*****************************************************************************/ +/* + * The following are the first-level codegen routines that analyze the format + * of each bytecode then either dispatch special purpose codegen routines + * or produce corresponding Thumb instructions directly. + */ + +static bool handleFmt10t_Fmt20t_Fmt30t(CompilationUnit *cUnit, MIR *mir, + BasicBlock *bb, Armv5teLIR *labelList) +{ + /* For OP_GOTO, OP_GOTO_16, and OP_GOTO_32 */ + genUnconditionalBranch(cUnit, &labelList[bb->taken->id]); + return false; +} + +static bool handleFmt10x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + if (((dalvikOpCode >= OP_UNUSED_3E) && (dalvikOpCode <= OP_UNUSED_43)) || + ((dalvikOpCode >= OP_UNUSED_E3) && (dalvikOpCode <= OP_UNUSED_EC))) { + LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode); + return true; + } + switch (dalvikOpCode) { + case OP_RETURN_VOID: + genReturnCommon(cUnit,mir); + break; + case OP_UNUSED_73: + case OP_UNUSED_79: + case OP_UNUSED_7A: + LOGE("Codegen: got unused opcode 0x%x\n",dalvikOpCode); + return true; + case OP_NOP: + break; + default: + return true; + } + return false; +} + +static bool handleFmt11n_Fmt31i(CompilationUnit *cUnit, MIR *mir) +{ + switch (mir->dalvikInsn.opCode) { + case OP_CONST: + case OP_CONST_4: + loadConstant(cUnit, r0, mir->dalvikInsn.vB); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + case OP_CONST_WIDE_32: + loadConstant(cUnit, r0, mir->dalvikInsn.vB); + newLIR3(cUnit, ARMV5TE_ASR, r1, r0, 31); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt21h(CompilationUnit *cUnit, MIR *mir) +{ + switch (mir->dalvikInsn.opCode) { + case OP_CONST_HIGH16: + loadConstant(cUnit, r0, mir->dalvikInsn.vB << 16); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + case OP_CONST_WIDE_HIGH16: + loadConstant(cUnit, r1, mir->dalvikInsn.vB << 16); + loadConstant(cUnit, r0, 0); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt20bc(CompilationUnit *cUnit, MIR *mir) +{ + /* For OP_THROW_VERIFICATION_ERROR */ + genInterpSingleStep(cUnit, mir); + return false; +} + +static bool handleFmt21c_Fmt31c(CompilationUnit *cUnit, MIR *mir) +{ + switch (mir->dalvikInsn.opCode) { + /* + * TODO: Verify that we can ignore the resolution check here because + * it will have already successfully been interpreted once + */ + case OP_CONST_STRING_JUMBO: + case OP_CONST_STRING: { + void *strPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResStrings[mir->dalvikInsn.vB]); + assert(strPtr != NULL); + loadConstant(cUnit, r0, (int) strPtr ); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + /* + * TODO: Verify that we can ignore the resolution check here because + * it will have already successfully been interpreted once + */ + case OP_CONST_CLASS: { + void *classPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); + assert(classPtr != NULL); + loadConstant(cUnit, r0, (int) classPtr ); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_SGET_OBJECT: + case OP_SGET_BOOLEAN: + case OP_SGET_CHAR: + case OP_SGET_BYTE: + case OP_SGET_SHORT: + case OP_SGET: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadConstant(cUnit, r0, (int) fieldPtr + valOffset); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, 0); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r2); + break; + } + case OP_SGET_WIDE: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadConstant(cUnit, r2, (int) fieldPtr + valOffset); + newLIR2(cUnit, ARMV5TE_LDMIA, r2, (1<<r0 | 1<<r1)); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + } + case OP_SPUT_OBJECT: + case OP_SPUT_BOOLEAN: + case OP_SPUT_CHAR: + case OP_SPUT_BYTE: + case OP_SPUT_SHORT: + case OP_SPUT: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadValue(cUnit, mir->dalvikInsn.vA, r0); + loadConstant(cUnit, r1, (int) fieldPtr + valOffset); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, r1, 0); + break; + } + case OP_SPUT_WIDE: { + int valOffset = (int)&((struct StaticField*)NULL)->value; + void *fieldPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vB]); + assert(fieldPtr != NULL); + loadValuePair(cUnit, mir->dalvikInsn.vA, r0, r1); + loadConstant(cUnit, r2, (int) fieldPtr + valOffset); + newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1<<r1)); + break; + } + case OP_NEW_INSTANCE: { + ClassObject *classPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); + assert(classPtr != NULL); + assert(classPtr->status & CLASS_INITIALIZED); + if ((classPtr->accessFlags & (ACC_INTERFACE|ACC_ABSTRACT)) != 0) { + /* It's going to throw, just let the interp. deal with it. */ + genInterpSingleStep(cUnit, mir); + return false; + } + loadConstant(cUnit, r0, (int) classPtr); + loadConstant(cUnit, r4PC, (int)dvmAllocObject); + genExportPC(cUnit, mir, r2, r3 ); + loadConstant(cUnit, r1, ALLOC_DONT_TRACK); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* + * TODO: As coded, we'll bail and reinterpret on alloc failure. + * Need a general mechanism to bail to thrown exception code. + */ + genNullCheck(cUnit, r0, mir->offset, NULL); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_CHECK_CAST: { + ClassObject *classPtr = + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]); + loadConstant(cUnit, r1, (int) classPtr ); + loadValue(cUnit, mir->dalvikInsn.vA, r0); /* Ref */ + /* + * TODO - in theory classPtr should be resoved by the time this + * instruction made into a trace, but we are seeing NULL at runtime + * so this check is temporarily used as a workaround. + */ + Armv5teLIR * pcrLabel = genNullCheck(cUnit, r1, mir->offset, NULL); + newLIR2(cUnit, ARMV5TE_CMP_RI8, r0, 0); /* Null? */ + Armv5teLIR *branch1 = + newLIR2(cUnit, ARMV5TE_B_COND, 4, ARM_COND_EQ); + /* r0 now contains object->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial); + newLIR2(cUnit, ARMV5TE_CMP_RR, r0, r1); + Armv5teLIR *branch2 = + newLIR2(cUnit, ARMV5TE_B_COND, 2, ARM_COND_EQ); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* check cast failed - punt to the interpreter */ + genNullCheck(cUnit, r0, mir->offset, pcrLabel); + /* check cast passed - branch target here */ + Armv5teLIR *target = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL); + branch1->generic.target = (LIR *)target; + branch2->generic.target = (LIR *)target; + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt11x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + switch (dalvikOpCode) { + case OP_MOVE_EXCEPTION: { + int offset = offsetof(InterpState, self); + int exOffset = offsetof(Thread, exception); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, r0, exOffset >> 2); + storeValue(cUnit, r1, mir->dalvikInsn.vA, r0); + break; + } + case OP_MOVE_RESULT: + case OP_MOVE_RESULT_OBJECT: { + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + case OP_MOVE_RESULT_WIDE: { + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE, (offset >> 2)+1); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + } + case OP_RETURN_WIDE: { + loadValuePair(cUnit, mir->dalvikInsn.vA, r0, r1); + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r1, rGLUE, (offset >> 2)+1); + genReturnCommon(cUnit,mir); + break; + } + case OP_RETURN: + case OP_RETURN_OBJECT: { + loadValue(cUnit, mir->dalvikInsn.vA, r0); + int offset = offsetof(InterpState, retval); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rGLUE, offset >> 2); + genReturnCommon(cUnit,mir); + break; + } + /* + * TODO-VERIFY: May be playing a bit fast and loose here. As coded, + * a failure on lock/unlock will cause us to revert to the interpeter + * to try again. This means we essentially ignore the first failure on + * the assumption that the interpreter will correctly handle the 2nd. + */ + case OP_MONITOR_ENTER: + case OP_MONITOR_EXIT: { + int offset = offsetof(InterpState, self); + loadValue(cUnit, mir->dalvikInsn.vA, r1); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, offset >> 2); + if (dalvikOpCode == OP_MONITOR_ENTER) { + loadConstant(cUnit, r2, (int)dvmLockObject); + } else { + loadConstant(cUnit, r2, (int)dvmUnlockObject); + } + /* + * TODO-VERIFY: Note that we're not doing an EXPORT_PC, as + * Lock/unlock won't throw, and this code does not support + * DEADLOCK_PREDICTION or MONITOR_TRACKING. Should it? + */ + genNullCheck(cUnit, r1, mir->offset, NULL); + /* Do the call */ + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + break; + } + case OP_THROW: { + genInterpSingleStep(cUnit, mir); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt12x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vSrc1Dest = mir->dalvikInsn.vA; + int vSrc2 = mir->dalvikInsn.vB; + + /* TODO - find the proper include file to declare these */ + float __aeabi_i2f( int op1 ); + int __aeabi_f2iz( float op1 ); + float __aeabi_d2f( double op1 ); + double __aeabi_f2d( float op1 ); + double __aeabi_i2d( int op1 ); + int __aeabi_d2iz( double op1 ); + long __aeabi_f2lz( float op1 ); + float __aeabi_l2f( long op1 ); + long __aeabi_d2lz( double op1 ); + double __aeabi_l2d( long op1 ); + + if ( (opCode >= OP_ADD_INT_2ADDR) && (opCode <= OP_REM_DOUBLE_2ADDR)) { + return genArithOp( cUnit, mir ); + } + + switch (opCode) { + case OP_INT_TO_FLOAT: + return genConversion(cUnit, mir, (void*)__aeabi_i2f, 1, 1); + case OP_FLOAT_TO_INT: + return genConversion(cUnit, mir, (void*)__aeabi_f2iz, 1, 1); + case OP_DOUBLE_TO_FLOAT: + return genConversion(cUnit, mir, (void*)__aeabi_d2f, 2, 1); + case OP_FLOAT_TO_DOUBLE: + return genConversion(cUnit, mir, (void*)__aeabi_f2d, 1, 2); + case OP_INT_TO_DOUBLE: + return genConversion(cUnit, mir, (void*)__aeabi_i2d, 1, 2); + case OP_DOUBLE_TO_INT: + return genConversion(cUnit, mir, (void*)__aeabi_d2iz, 2, 1); + case OP_FLOAT_TO_LONG: + return genConversion(cUnit, mir, (void*)__aeabi_f2lz, 1, 2); + case OP_LONG_TO_FLOAT: + return genConversion(cUnit, mir, (void*)__aeabi_l2f, 2, 1); + case OP_DOUBLE_TO_LONG: + return genConversion(cUnit, mir, (void*)__aeabi_d2lz, 2, 2); + case OP_LONG_TO_DOUBLE: + return genConversion(cUnit, mir, (void*)__aeabi_l2d, 2, 2); + case OP_NEG_INT: + case OP_NOT_INT: + return genArithOpInt(cUnit, mir, vSrc1Dest, vSrc1Dest, vSrc2); + case OP_NEG_LONG: + case OP_NOT_LONG: + return genArithOpLong(cUnit,mir, vSrc1Dest, vSrc1Dest, vSrc2); + case OP_NEG_FLOAT: + return genArithOpFloat(cUnit,mir,vSrc1Dest,vSrc1Dest,vSrc2); + case OP_NEG_DOUBLE: + return genArithOpDouble(cUnit,mir,vSrc1Dest,vSrc1Dest,vSrc2); + case OP_MOVE_WIDE: + loadValuePair(cUnit, mir->dalvikInsn.vB, r0, r1); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + case OP_INT_TO_LONG: + loadValue(cUnit, mir->dalvikInsn.vB, r0); + newLIR3(cUnit, ARMV5TE_ASR, r1, r0, 31); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + break; + case OP_MOVE: + case OP_MOVE_OBJECT: + case OP_LONG_TO_INT: + loadValue(cUnit, vSrc2, r0); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_INT_TO_BYTE: + loadValue(cUnit, vSrc2, r0); + newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 24); + newLIR3(cUnit, ARMV5TE_ASR, r0, r0, 24); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_INT_TO_SHORT: + loadValue(cUnit, vSrc2, r0); + newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 16); + newLIR3(cUnit, ARMV5TE_ASR, r0, r0, 16); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_INT_TO_CHAR: + loadValue(cUnit, vSrc2, r0); + newLIR3(cUnit, ARMV5TE_LSL, r0, r0, 16); + newLIR3(cUnit, ARMV5TE_LSR, r0, r0, 16); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_ARRAY_LENGTH: { + int lenOffset = offsetof(ArrayObject, length); + loadValue(cUnit, vSrc2, r0); + genNullCheck(cUnit, r0, mir->offset, NULL); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, lenOffset >> 2); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt21s(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + /* It takes few instructions to handle OP_CONST_WIDE_16 inline */ + if (dalvikOpCode == OP_CONST_WIDE_16) { + int rDest = mir->dalvikInsn.vA; + int BBBB = mir->dalvikInsn.vB; + int rLow = r0, rHigh = r1; + if (BBBB == 0) { + newLIR2(cUnit, ARMV5TE_MOV_IMM, rLow, 0); + rHigh = rLow; + } else if (BBBB > 0 && BBBB <= 255) { + /* rLow = ssssBBBB */ + newLIR2(cUnit, ARMV5TE_MOV_IMM, rLow, BBBB); + /* rHigh = 0 */ + newLIR2(cUnit, ARMV5TE_MOV_IMM, rHigh, 0); + } else { + loadConstant(cUnit, rLow, BBBB); + /* + * arithmetic-shift-right 32 bits to get the high half of long + * [63..32] + */ + newLIR3(cUnit, ARMV5TE_ASR, rHigh, rLow, 0); + } + + /* Save the long values to the specified Dalvik register pair */ + /* + * If rDest is no greater than 30, use two "str rd, [rFP + immed_5]" + * instructions to store the results. Effective address is + * rFP + immed_5 << 2. + */ + if (rDest < 31) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, rLow, rFP, rDest); + newLIR3(cUnit, ARMV5TE_STR_RRI5, rHigh, rFP, rDest+1); + } else { + /* + * Otherwise just load the frame offset from the constant pool and add + * it to rFP. Then use stmia to store the results to the specified + * register pair. + */ + /* Need to replicate the content in r0 to r1 */ + if (rLow == rHigh) { + newLIR3(cUnit, ARMV5TE_ADD_RRI3, rLow+1, rLow, 0); + } + /* load the rFP offset into r2 */ + loadConstant(cUnit, r2, rDest*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, rFP, r2); + newLIR2(cUnit, ARMV5TE_STMIA, r2, (1<<r0 | 1 << r1)); + } + } else if (dalvikOpCode == OP_CONST_16) { + int rDest = mir->dalvikInsn.vA; + int BBBB = mir->dalvikInsn.vB; + if (BBBB >= 0 && BBBB <= 255) { + /* r0 = BBBB */ + newLIR2(cUnit, ARMV5TE_MOV_IMM, r0, BBBB); + } else { + loadConstant(cUnit, r0, BBBB); + } + + /* Save the constant to the specified Dalvik register */ + /* + * If rDest is no greater than 31, effective address is + * rFP + immed_5 << 2. + */ + if (rDest < 32) { + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, rFP, rDest); + } else { + /* + * Otherwise just load the frame offset from the constant pool and add + * it to rFP. Then use stmia to store the results to the specified + * register pair. + */ + /* load the rFP offset into r2 */ + loadConstant(cUnit, r2, rDest*4); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r2, rFP, r2); + newLIR3(cUnit, ARMV5TE_STR_RRI5, r0, r2, 0); + } + } else { + return true; + } + return false; +} + +/* Compare agaist zero */ +static bool handleFmt21t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + Armv5teConditionCode cond; + + loadValue(cUnit, mir->dalvikInsn.vA, r0); + newLIR2(cUnit, ARMV5TE_CMP_RI8, r0, 0); + + switch (dalvikOpCode) { + case OP_IF_EQZ: + cond = ARM_COND_EQ; + break; + case OP_IF_NEZ: + cond = ARM_COND_NE; + break; + case OP_IF_LTZ: + cond = ARM_COND_LT; + break; + case OP_IF_GEZ: + cond = ARM_COND_GE; + break; + case OP_IF_GTZ: + cond = ARM_COND_GT; + break; + case OP_IF_LEZ: + cond = ARM_COND_LE; + break; + default: + cond = 0; + LOGE("Unexpected opcode (%d) for Fmt21t\n", dalvikOpCode); + dvmAbort(); + } + genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]); + /* This mostly likely will be optimized away in a later phase */ + genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); + return false; +} + +static bool handleFmt22b_Fmt22s(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + int vSrc = mir->dalvikInsn.vB; + int vDest = mir->dalvikInsn.vA; + int lit = mir->dalvikInsn.vC; + int armOp; + + /* TODO: find the proper .h file to declare these */ + int __aeabi_idivmod(int op1, int op2); + int __aeabi_idiv(int op1, int op2); + + switch (dalvikOpCode) { + case OP_ADD_INT_LIT8: + case OP_ADD_INT_LIT16: + loadValue(cUnit, vSrc, r0); + if (lit <= 255 && lit >= 0) { + newLIR2(cUnit, ARMV5TE_ADD_RI8, r0, lit); + storeValue(cUnit, r0, vDest, r1); + } else if (lit >= -255 && lit <= 0) { + /* Convert to a small constant subtraction */ + newLIR2(cUnit, ARMV5TE_SUB_RI8, r0, -lit); + storeValue(cUnit, r0, vDest, r1); + } else { + loadConstant(cUnit, r1, lit); + genBinaryOp(cUnit, vDest, ARMV5TE_ADD_RRR); + } + break; + + case OP_RSUB_INT_LIT8: + case OP_RSUB_INT: + loadValue(cUnit, vSrc, r1); + loadConstant(cUnit, r0, lit); + genBinaryOp(cUnit, vDest, ARMV5TE_SUB_RRR); + break; + + case OP_MUL_INT_LIT8: + case OP_MUL_INT_LIT16: + case OP_AND_INT_LIT8: + case OP_AND_INT_LIT16: + case OP_OR_INT_LIT8: + case OP_OR_INT_LIT16: + case OP_XOR_INT_LIT8: + case OP_XOR_INT_LIT16: + loadValue(cUnit, vSrc, r0); + loadConstant(cUnit, r1, lit); + switch (dalvikOpCode) { + case OP_MUL_INT_LIT8: + case OP_MUL_INT_LIT16: + armOp = ARMV5TE_MUL; + break; + case OP_AND_INT_LIT8: + case OP_AND_INT_LIT16: + armOp = ARMV5TE_AND_RR; + break; + case OP_OR_INT_LIT8: + case OP_OR_INT_LIT16: + armOp = ARMV5TE_ORR; + break; + case OP_XOR_INT_LIT8: + case OP_XOR_INT_LIT16: + armOp = ARMV5TE_EOR; + break; + default: + dvmAbort(); + } + genBinaryOp(cUnit, vDest, armOp); + break; + + case OP_SHL_INT_LIT8: + case OP_SHR_INT_LIT8: + case OP_USHR_INT_LIT8: + loadValue(cUnit, vSrc, r0); + switch (dalvikOpCode) { + case OP_SHL_INT_LIT8: + armOp = ARMV5TE_LSL; + break; + case OP_SHR_INT_LIT8: + armOp = ARMV5TE_ASR; + break; + case OP_USHR_INT_LIT8: + armOp = ARMV5TE_LSR; + break; + default: dvmAbort(); + } + newLIR3(cUnit, armOp, r0, r0, lit); + storeValue(cUnit, r0, vDest, r1); + break; + + case OP_DIV_INT_LIT8: + case OP_DIV_INT_LIT16: + if (lit == 0) { + /* Let the interpreter deal with div by 0 */ + genInterpSingleStep(cUnit, mir); + return false; + } + loadConstant(cUnit, r2, (int)__aeabi_idiv); + loadConstant(cUnit, r1, lit); + loadValue(cUnit, vSrc, r0); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, r0, vDest, r2); + break; + + case OP_REM_INT_LIT8: + case OP_REM_INT_LIT16: + if (lit == 0) { + /* Let the interpreter deal with div by 0 */ + genInterpSingleStep(cUnit, mir); + return false; + } + loadConstant(cUnit, r2, (int)__aeabi_idivmod); + loadConstant(cUnit, r1, lit); + loadValue(cUnit, vSrc, r0); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + storeValue(cUnit, r1, vDest, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt22c(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + int fieldOffset; + + if (dalvikOpCode >= OP_IGET && dalvikOpCode <= OP_IPUT_SHORT) { + InstField *pInstField = (InstField *) + cUnit->method->clazz->pDvmDex->pResFields[mir->dalvikInsn.vC]; + int fieldOffset; + + assert(pInstField != NULL); + fieldOffset = pInstField->byteOffset; + } else { + /* To make the compiler happy */ + fieldOffset = 0; + } + switch (dalvikOpCode) { + /* + * TODO: I may be assuming too much here. + * Verify what is known at JIT time. + */ + case OP_NEW_ARRAY: { + void *classPtr = (void*) + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); + assert(classPtr != NULL); + loadValue(cUnit, mir->dalvikInsn.vB, r1); /* Len */ + loadConstant(cUnit, r0, (int) classPtr ); + loadConstant(cUnit, r4PC, (int)dvmAllocArrayByClass); + Armv5teLIR *pcrLabel = + genRegImmCheck(cUnit, ARM_COND_MI, r1, 0, mir->offset, NULL); + genExportPC(cUnit, mir, r2, r3 ); + newLIR2(cUnit, ARMV5TE_MOV_IMM,r2,ALLOC_DONT_TRACK); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* + * TODO: As coded, we'll bail and reinterpret on alloc failure. + * Need a general mechanism to bail to thrown exception code. + */ + genNullCheck(cUnit, r0, mir->offset, pcrLabel); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + break; + } + /* + * TODO: I may be assuming too much here. + * Verify what is known at JIT time. + */ + case OP_INSTANCE_OF: { + ClassObject *classPtr = + (cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vC]); + assert(classPtr != NULL); + loadValue(cUnit, mir->dalvikInsn.vB, r1); /* Ref */ + loadConstant(cUnit, r2, (int) classPtr ); + loadConstant(cUnit, r0, 1); /* Assume true */ + newLIR2(cUnit, ARMV5TE_CMP_RI8, r1, 0); /* Null? */ + Armv5teLIR *branch1 = newLIR2(cUnit, ARMV5TE_B_COND, 4, + ARM_COND_EQ); + /* r1 now contains object->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, r1, + offsetof(Object, clazz) >> 2); + loadConstant(cUnit, r4PC, (int)dvmInstanceofNonTrivial); + newLIR2(cUnit, ARMV5TE_CMP_RR, r1, r2); + Armv5teLIR *branch2 = newLIR2(cUnit, ARMV5TE_B_COND, 2, + ARM_COND_EQ); + newLIR2(cUnit, ARMV5TE_MOV_RR, r0, r1); + newLIR2(cUnit, ARMV5TE_MOV_RR, r1, r2); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + /* branch target here */ + Armv5teLIR *target = newLIR0(cUnit, ARMV5TE_PSEUDO_TARGET_LABEL); + storeValue(cUnit, r0, mir->dalvikInsn.vA, r1); + branch1->generic.target = (LIR *)target; + branch2->generic.target = (LIR *)target; + break; + } + case OP_IGET_WIDE: + genIGetWide(cUnit, mir, fieldOffset); + break; + case OP_IGET: + case OP_IGET_OBJECT: + genIGet(cUnit, mir, ARMV5TE_LDR_RRR, fieldOffset); + break; + case OP_IGET_BOOLEAN: + genIGet(cUnit, mir, ARMV5TE_LDRB_RRR, fieldOffset); + break; + case OP_IGET_BYTE: + genIGet(cUnit, mir, ARMV5TE_LDRSB_RRR, fieldOffset); + break; + case OP_IGET_CHAR: + genIGet(cUnit, mir, ARMV5TE_LDRH_RRR, fieldOffset); + break; + case OP_IGET_SHORT: + genIGet(cUnit, mir, ARMV5TE_LDRSH_RRR, fieldOffset); + break; + case OP_IPUT_WIDE: + genIPutWide(cUnit, mir, fieldOffset); + break; + case OP_IPUT: + case OP_IPUT_OBJECT: + genIPut(cUnit, mir, ARMV5TE_STR_RRR, fieldOffset); + break; + case OP_IPUT_SHORT: + case OP_IPUT_CHAR: + genIPut(cUnit, mir, ARMV5TE_STRH_RRR, fieldOffset); + break; + case OP_IPUT_BYTE: + case OP_IPUT_BOOLEAN: + genIPut(cUnit, mir, ARMV5TE_STRB_RRR, fieldOffset); + break; + default: + return true; + } + return false; +} + +static bool handleFmt22cs(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + int fieldOffset = mir->dalvikInsn.vC; + switch (dalvikOpCode) { + case OP_IGET_QUICK: + case OP_IGET_OBJECT_QUICK: + genIGet(cUnit, mir, ARMV5TE_LDR_RRR, fieldOffset); + break; + case OP_IPUT_QUICK: + case OP_IPUT_OBJECT_QUICK: + genIPut(cUnit, mir, ARMV5TE_STR_RRR, fieldOffset); + break; + case OP_IGET_WIDE_QUICK: + genIGetWide(cUnit, mir, fieldOffset); + break; + case OP_IPUT_WIDE_QUICK: + genIPutWide(cUnit, mir, fieldOffset); + break; + default: + return true; + } + return false; + +} + +/* Compare agaist zero */ +static bool handleFmt22t(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + Armv5teConditionCode cond; + + loadValue(cUnit, mir->dalvikInsn.vA, r0); + loadValue(cUnit, mir->dalvikInsn.vB, r1); + newLIR2(cUnit, ARMV5TE_CMP_RR, r0, r1); + + switch (dalvikOpCode) { + case OP_IF_EQ: + cond = ARM_COND_EQ; + break; + case OP_IF_NE: + cond = ARM_COND_NE; + break; + case OP_IF_LT: + cond = ARM_COND_LT; + break; + case OP_IF_GE: + cond = ARM_COND_GE; + break; + case OP_IF_GT: + cond = ARM_COND_GT; + break; + case OP_IF_LE: + cond = ARM_COND_LE; + break; + default: + cond = 0; + LOGE("Unexpected opcode (%d) for Fmt22t\n", dalvikOpCode); + dvmAbort(); + } + genConditionalBranch(cUnit, cond, &labelList[bb->taken->id]); + /* This mostly likely will be optimized away in a later phase */ + genUnconditionalBranch(cUnit, &labelList[bb->fallThrough->id]); + return false; +} + +static bool handleFmt22x_Fmt32x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vSrc1Dest = mir->dalvikInsn.vA; + int vSrc2 = mir->dalvikInsn.vB; + + switch (opCode) { + case OP_MOVE_16: + case OP_MOVE_OBJECT_16: + case OP_MOVE_FROM16: + case OP_MOVE_OBJECT_FROM16: + loadValue(cUnit, vSrc2, r0); + storeValue(cUnit, r0, vSrc1Dest, r1); + break; + case OP_MOVE_WIDE_16: + case OP_MOVE_WIDE_FROM16: + loadValuePair(cUnit, vSrc2, r0, r1); + storeValuePair(cUnit, r0, r1, vSrc1Dest, r2); + break; + default: + return true; + } + return false; +} + +static bool handleFmt23x(CompilationUnit *cUnit, MIR *mir) +{ + OpCode opCode = mir->dalvikInsn.opCode; + int vA = mir->dalvikInsn.vA; + int vB = mir->dalvikInsn.vB; + int vC = mir->dalvikInsn.vC; + + if ( (opCode >= OP_ADD_INT) && (opCode <= OP_REM_DOUBLE)) { + return genArithOp( cUnit, mir ); + } + + switch (opCode) { + case OP_CMP_LONG: + loadValuePair(cUnit,vB, r0, r1); + loadValuePair(cUnit, vC, r2, r3); + genDispatchToHandler(cUnit, TEMPLATE_CMP_LONG); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPL_FLOAT: + loadValue(cUnit, vB, r0); + loadValue(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPL_FLOAT); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPG_FLOAT: + loadValue(cUnit, vB, r0); + loadValue(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPG_FLOAT); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPL_DOUBLE: + loadValueAddress(cUnit, vB, r0); + loadValueAddress(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPL_DOUBLE); + storeValue(cUnit, r0, vA, r1); + break; + case OP_CMPG_DOUBLE: + loadValueAddress(cUnit, vB, r0); + loadValueAddress(cUnit, vC, r1); + genDispatchToHandler(cUnit, TEMPLATE_CMPG_DOUBLE); + storeValue(cUnit, r0, vA, r1); + break; + case OP_AGET_WIDE: + genArrayGet(cUnit, mir, ARMV5TE_LDR_RRR, vB, vC, vA, 3); + break; + case OP_AGET: + case OP_AGET_OBJECT: + genArrayGet(cUnit, mir, ARMV5TE_LDR_RRR, vB, vC, vA, 2); + break; + case OP_AGET_BOOLEAN: + genArrayGet(cUnit, mir, ARMV5TE_LDRB_RRR, vB, vC, vA, 0); + break; + case OP_AGET_BYTE: + genArrayGet(cUnit, mir, ARMV5TE_LDRSB_RRR, vB, vC, vA, 0); + break; + case OP_AGET_CHAR: + genArrayGet(cUnit, mir, ARMV5TE_LDRH_RRR, vB, vC, vA, 1); + break; + case OP_AGET_SHORT: + genArrayGet(cUnit, mir, ARMV5TE_LDRSH_RRR, vB, vC, vA, 1); + break; + case OP_APUT_WIDE: + genArrayPut(cUnit, mir, ARMV5TE_STR_RRR, vB, vC, vA, 3); + break; + case OP_APUT: + case OP_APUT_OBJECT: + genArrayPut(cUnit, mir, ARMV5TE_STR_RRR, vB, vC, vA, 2); + break; + case OP_APUT_SHORT: + case OP_APUT_CHAR: + genArrayPut(cUnit, mir, ARMV5TE_STRH_RRR, vB, vC, vA, 1); + break; + case OP_APUT_BYTE: + case OP_APUT_BOOLEAN: + genArrayPut(cUnit, mir, ARMV5TE_STRB_RRR, vB, vC, vA, 0); + break; + default: + return true; + } + return false; +} + +static bool handleFmt31t(CompilationUnit *cUnit, MIR *mir) +{ + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + switch (dalvikOpCode) { + case OP_FILL_ARRAY_DATA: { + loadConstant(cUnit, r4PC, (int)dvmInterpHandleFillArrayData); + loadValue(cUnit, mir->dalvikInsn.vA, r0); + loadConstant(cUnit, r1, (mir->dalvikInsn.vB << 1) + + (int) (cUnit->method->insns + mir->offset)); + genExportPC(cUnit, mir, r2, r3 ); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + genNullCheck(cUnit, r0, mir->offset, NULL); + break; + } + /* + * TODO + * - Add a 1 to 3-entry per-location cache here to completely + * bypass the dvmInterpHandle[Packed/Sparse]Switch call w/ chaining + * - Use out-of-line handlers for both of these + */ + case OP_PACKED_SWITCH: + case OP_SPARSE_SWITCH: { + if (dalvikOpCode == OP_PACKED_SWITCH) { + loadConstant(cUnit, r4PC, (int)dvmInterpHandlePackedSwitch); + } else { + loadConstant(cUnit, r4PC, (int)dvmInterpHandleSparseSwitch); + } + loadValue(cUnit, mir->dalvikInsn.vA, r1); + loadConstant(cUnit, r0, (mir->dalvikInsn.vB << 1) + + (int) (cUnit->method->insns + mir->offset)); + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + loadConstant(cUnit, r1, (int)(cUnit->method->insns + mir->offset)); + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r2, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNoChain) + >> 2); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r0, r0, r0); + newLIR3(cUnit, ARMV5TE_ADD_RRR, r4PC, r0, r1); + newLIR1(cUnit, ARMV5TE_BLX_R, r2); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt35c_3rc(CompilationUnit *cUnit, MIR *mir, BasicBlock *bb, + Armv5teLIR *labelList) +{ + Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id]; + Armv5teLIR *pcrLabel = NULL; + + DecodedInstruction *dInsn = &mir->dalvikInsn; + switch (mir->dalvikInsn.opCode) { + /* + * calleeMethod = this->clazz->vtable[ + * method->clazz->pDvmDex->pResMethods[BBBB]->methodIndex + * ] + */ + case OP_INVOKE_VIRTUAL: + case OP_INVOKE_VIRTUAL_RANGE: { + int methodIndex = + cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]-> + methodIndex; + + if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 now contains this->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + /* r0 now contains this->clazz->vtable */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(ClassObject, vtable) >> 2); + addrRetChain->generic.target = (LIR *) retChainingCell; + + if (methodIndex < 32) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, methodIndex); + } else { + loadConstant(cUnit, r7, methodIndex<<2); + newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r7); + } + + /* + * r0 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); + break; + } + /* + * calleeMethod = method->clazz->super->vtable[method->clazz->pDvmDex + * ->pResMethods[BBBB]->methodIndex] + */ + /* TODO - not excersized in RunPerf.jar */ + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: { + int mIndex = cUnit->method->clazz->pDvmDex-> + pResMethods[dInsn->vB]->methodIndex; + const Method *calleeMethod = + cUnit->method->clazz->super->vtable[mIndex]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: { + const Method *calleeMethod = + cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_DIRECT) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* calleeMethod = method->clazz->pDvmDex->pResMethods[BBBB] */ + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: { + const Method *calleeMethod = + cUnit->method->clazz->pDvmDex->pResMethods[dInsn->vB]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_STATIC) + genProcessArgsNoRange(cUnit, mir, dInsn, + NULL /* no null check */); + else + genProcessArgsRange(cUnit, mir, dInsn, + NULL /* no null check */); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* + * calleeMethod = dvmFindInterfaceMethodInCache(this->clazz, + * BBBB, method, method->clazz->pDvmDex) + */ + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: { + int methodIndex = dInsn->vB; + + if (mir->dalvikInsn.opCode == OP_INVOKE_INTERFACE) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 now contains this->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + + /* r1 = BBBB */ + loadConstant(cUnit, r1, dInsn->vB); + + /* r2 = method (caller) */ + loadConstant(cUnit, r2, (int) cUnit->method); + + /* r3 = pDvmDex */ + loadConstant(cUnit, r3, (int) cUnit->method->clazz->pDvmDex); + + loadConstant(cUnit, r7, + (intptr_t) dvmFindInterfaceMethodInCache); + newLIR1(cUnit, ARMV5TE_BLX_R, r7); + + /* r0 = calleeMethod (returned from dvmFindInterfaceMethodInCache */ + + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + addrRetChain->generic.target = (LIR *) retChainingCell; + /* + * r0 = this, r1 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); + break; + } + /* NOP */ + case OP_INVOKE_DIRECT_EMPTY: { + return false; + } + case OP_FILLED_NEW_ARRAY: + case OP_FILLED_NEW_ARRAY_RANGE: { + /* Just let the interpreter deal with these */ + genInterpSingleStep(cUnit, mir); + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt35ms_3rms(CompilationUnit *cUnit, MIR *mir, + BasicBlock *bb, Armv5teLIR *labelList) +{ + Armv5teLIR *retChainingCell = &labelList[bb->fallThrough->id]; + Armv5teLIR *pcrLabel = NULL; + + DecodedInstruction *dInsn = &mir->dalvikInsn; + switch (mir->dalvikInsn.opCode) { + /* calleeMethod = this->clazz->vtable[BBBB] */ + case OP_INVOKE_VIRTUAL_QUICK_RANGE: + case OP_INVOKE_VIRTUAL_QUICK: { + int methodIndex = dInsn->vB; + if (mir->dalvikInsn.opCode == OP_INVOKE_VIRTUAL_QUICK) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 now contains this->clazz */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(Object, clazz) >> 2); + /* r1 = &retChainingCell */ + Armv5teLIR *addrRetChain = newLIR2(cUnit, ARMV5TE_ADD_PC_REL, + r1, 0); + /* r4PC = dalvikCallsite */ + loadConstant(cUnit, r4PC, + (int) (cUnit->method->insns + mir->offset)); + + /* r0 now contains this->clazz->vtable */ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, + offsetof(ClassObject, vtable) >> 2); + addrRetChain->generic.target = (LIR *) retChainingCell; + + if (methodIndex < 32) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, r0, methodIndex); + } else { + loadConstant(cUnit, r7, methodIndex<<2); + newLIR3(cUnit, ARMV5TE_LDR_RRR, r0, r0, r7); + } + + /* + * r0 = calleeMethod, + * r1 = &ChainingCell, + * r4PC = callsiteDPC, + */ + genDispatchToHandler(cUnit, TEMPLATE_INVOKE_METHOD_NO_OPT); +#if defined(INVOKE_STATS) + gDvmJit.invokeNoOpt++; +#endif + break; + } + /* calleeMethod = method->clazz->super->vtable[BBBB] */ + case OP_INVOKE_SUPER_QUICK: + case OP_INVOKE_SUPER_QUICK_RANGE: { + const Method *calleeMethod = + cUnit->method->clazz->super->vtable[dInsn->vB]; + + if (mir->dalvikInsn.opCode == OP_INVOKE_SUPER_QUICK) + genProcessArgsNoRange(cUnit, mir, dInsn, &pcrLabel); + else + genProcessArgsRange(cUnit, mir, dInsn, &pcrLabel); + + /* r0 = calleeMethod */ + loadConstant(cUnit, r0, (int) calleeMethod); + + genInvokeCommon(cUnit, mir, bb, labelList, pcrLabel, + calleeMethod); + break; + } + /* calleeMethod = method->clazz->super->vtable[BBBB] */ + default: + return true; + } + /* Handle exceptions using the interpreter */ + genTrap(cUnit, mir->offset, pcrLabel); + return false; +} + +/* + * NOTE: We assume here that the special native inline routines + * are side-effect free. By making this assumption, we can safely + * re-execute the routine from the interpreter if it decides it + * wants to throw an exception. We still need to EXPORT_PC(), though. + */ +static bool handleFmt3inline(CompilationUnit *cUnit, MIR *mir) +{ + DecodedInstruction *dInsn = &mir->dalvikInsn; + switch( mir->dalvikInsn.opCode) { + case OP_EXECUTE_INLINE: { + unsigned int i; + const InlineOperation* inLineTable = dvmGetInlineOpsTable(); + int offset = (int) &((InterpState *) NULL)->retval; + int operation = dInsn->vB; + + if (!strcmp(inLineTable[operation].classDescriptor, + "Ljava/lang/String;") && + !strcmp(inLineTable[operation].methodName, + "length") && + !strcmp(inLineTable[operation].methodSignature, + "()I")) { + return genInlinedStringLength(cUnit,mir); + } + + /* Materialize pointer to retval & push */ + newLIR2(cUnit, ARMV5TE_MOV_RR, r4PC, rGLUE); + newLIR2(cUnit, ARMV5TE_ADD_RI8, r4PC, offset); + /* Push r4 and (just to take up space) r5) */ + newLIR1(cUnit, ARMV5TE_PUSH, (1<<r4PC | 1<<rFP)); + + /* Get code pointer to inline routine */ + loadConstant(cUnit, r4PC, (int)inLineTable[operation].func); + + /* Export PC */ + genExportPC(cUnit, mir, r0, r1 ); + + /* Load arguments to r0 through r3 as applicable */ + for (i=0; i < dInsn->vA; i++) { + loadValue(cUnit, dInsn->arg[i], i); + } + /* Call inline routine */ + newLIR1(cUnit, ARMV5TE_BLX_R, r4PC); + + /* Strip frame */ + newLIR1(cUnit, ARMV5TE_ADD_SPI7, 2); + + /* Did we throw? If so, redo under interpreter*/ + genNullCheck(cUnit, r0, mir->offset, NULL); + + break; + } + default: + return true; + } + return false; +} + +static bool handleFmt51l(CompilationUnit *cUnit, MIR *mir) +{ + loadConstant(cUnit, r0, mir->dalvikInsn.vB_wide & 0xFFFFFFFFUL); + loadConstant(cUnit, r1, (mir->dalvikInsn.vB_wide>>32) & 0xFFFFFFFFUL); + storeValuePair(cUnit, r0, r1, mir->dalvikInsn.vA, r2); + return false; +} + +/*****************************************************************************/ +/* + * The following are special processing routines that handle transfer of + * controls between compiled code and the interpreter. Certain VM states like + * Dalvik PC and special-purpose registers are reconstructed here. + */ + +/* Chaining cell for normal-ending compiles (eg branches) */ +static void handleGenericChainingCell(CompilationUnit *cUnit, + unsigned int offset) +{ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToInterpNormal) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r0); + addWordData(cUnit, (int) (cUnit->method->insns + offset), true); +} + +/* + * Chaining cell for instructions that immediately following a method + * invocation. + */ +static void handlePostInvokeChainingCell(CompilationUnit *cUnit, + unsigned int offset) +{ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r0); + addWordData(cUnit, (int) (cUnit->method->insns + offset), true); +} + +/* Chaining cell for monomorphic method invocations. */ +static void handleInvokeChainingCell(CompilationUnit *cUnit, + const Method *callee) +{ + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r0, rGLUE, + offsetof(InterpState, jitToInterpEntries.dvmJitToTraceSelect) >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r0); + addWordData(cUnit, (int) (callee->insns), true); +} + +/* Load the Dalvik PC into r0 and jump to the specified target */ +static void handlePCReconstruction(CompilationUnit *cUnit, + Armv5teLIR *targetLabel) +{ + Armv5teLIR **pcrLabel = + (Armv5teLIR **) cUnit->pcReconstructionList.elemList; + int numElems = cUnit->pcReconstructionList.numUsed; + int i; + for (i = 0; i < numElems; i++) { + dvmCompilerAppendLIR(cUnit, (LIR *) pcrLabel[i]); + /* r0 = dalvik PC */ + loadConstant(cUnit, r0, pcrLabel[i]->operands[0]); + genUnconditionalBranch(cUnit, targetLabel); + } +} + +/* Entry function to invoke the backend of the JIT compiler */ +void dvmCompilerMIR2LIR(CompilationUnit *cUnit) +{ + /* Used to hold the labels of each block */ + Armv5teLIR *labelList = + dvmCompilerNew(sizeof(Armv5teLIR) * cUnit->numBlocks, true); + GrowableList chainingListByType[CHAINING_CELL_LAST]; + int i; + + /* + * Initialize the three chaining lists for generic, post-invoke, and invoke + * chains. + */ + for (i = 0; i < CHAINING_CELL_LAST; i++) { + dvmInitGrowableList(&chainingListByType[i], 2); + } + + BasicBlock **blockList = cUnit->blockList; + + /* Handle the content in each basic block */ + for (i = 0; i < cUnit->numBlocks; i++) { + blockList[i]->visited = true; + MIR *mir; + + labelList[i].operands[0] = blockList[i]->startOffset; + + if (blockList[i]->blockType >= CHAINING_CELL_LAST) { + /* + * Append the label pseudo LIR first. Chaining cells will be handled + * separately afterwards. + */ + dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]); + } + + if (blockList[i]->blockType == DALVIK_BYTECODE) { + labelList[i].opCode = ARMV5TE_PSEUDO_NORMAL_BLOCK_LABEL; + } else { + switch (blockList[i]->blockType) { + case CHAINING_CELL_GENERIC: + labelList[i].opCode = ARMV5TE_PSEUDO_CHAINING_CELL_GENERIC; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_GENERIC], (void *) i); + break; + case CHAINING_CELL_INVOKE: + labelList[i].opCode = ARMV5TE_PSEUDO_CHAINING_CELL_INVOKE; + labelList[i].operands[0] = + (int) blockList[i]->containingMethod; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_INVOKE], (void *) i); + break; + case CHAINING_CELL_POST_INVOKE: + labelList[i].opCode = + ARMV5TE_PSEUDO_CHAINING_CELL_POST_INVOKE; + /* handle the codegen later */ + dvmInsertGrowableList( + &chainingListByType[CHAINING_CELL_POST_INVOKE], + (void *) i); + break; + case PC_RECONSTRUCTION: + /* Make sure exception handling block is next */ + labelList[i].opCode = + ARMV5TE_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL; + assert (i == cUnit->numBlocks - 2); + handlePCReconstruction(cUnit, &labelList[i+1]); + break; + case EXCEPTION_HANDLING: + labelList[i].opCode = ARMV5TE_PSEUDO_EH_BLOCK_LABEL; + if (cUnit->pcReconstructionList.numUsed) { + newLIR3(cUnit, ARMV5TE_LDR_RRI5, r1, rGLUE, + offsetof(InterpState, + jitToInterpEntries.dvmJitToInterpPunt) + >> 2); + newLIR1(cUnit, ARMV5TE_BLX_R, r1); + } + break; + default: + break; + } + continue; + } + for (mir = blockList[i]->firstMIRInsn; mir; mir = mir->next) { + OpCode dalvikOpCode = mir->dalvikInsn.opCode; + InstructionFormat dalvikFormat = + dexGetInstrFormat(gDvm.instrFormat, dalvikOpCode); + newLIR2(cUnit, ARMV5TE_PSEUDO_DALVIK_BYTECODE_BOUNDARY, + mir->offset,dalvikOpCode); + bool notHandled; + /* + * Debugging: screen the opcode first to see if it is in the + * do[-not]-compile list + */ + bool singleStepMe = + gDvmJit.includeSelectedOp != + ((gDvmJit.opList[dalvikOpCode >> 3] & + (1 << (dalvikOpCode & 0x7))) != + 0); + if (singleStepMe || cUnit->allSingleStep) { + notHandled = false; + genInterpSingleStep(cUnit, mir); + } else { + opcodeCoverage[dalvikOpCode]++; + switch (dalvikFormat) { + case kFmt10t: + case kFmt20t: + case kFmt30t: + notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit, + mir, blockList[i], labelList); + break; + case kFmt10x: + notHandled = handleFmt10x(cUnit, mir); + break; + case kFmt11n: + case kFmt31i: + notHandled = handleFmt11n_Fmt31i(cUnit, mir); + break; + case kFmt11x: + notHandled = handleFmt11x(cUnit, mir); + break; + case kFmt12x: + notHandled = handleFmt12x(cUnit, mir); + break; + case kFmt20bc: + notHandled = handleFmt20bc(cUnit, mir); + break; + case kFmt21c: + case kFmt31c: + notHandled = handleFmt21c_Fmt31c(cUnit, mir); + break; + case kFmt21h: + notHandled = handleFmt21h(cUnit, mir); + break; + case kFmt21s: + notHandled = handleFmt21s(cUnit, mir); + break; + case kFmt21t: + notHandled = handleFmt21t(cUnit, mir, blockList[i], + labelList); + break; + case kFmt22b: + case kFmt22s: + notHandled = handleFmt22b_Fmt22s(cUnit, mir); + break; + case kFmt22c: + notHandled = handleFmt22c(cUnit, mir); + break; + case kFmt22cs: + notHandled = handleFmt22cs(cUnit, mir); + break; + case kFmt22t: + notHandled = handleFmt22t(cUnit, mir, blockList[i], + labelList); + break; + case kFmt22x: + case kFmt32x: + notHandled = handleFmt22x_Fmt32x(cUnit, mir); + break; + case kFmt23x: + notHandled = handleFmt23x(cUnit, mir); + break; + case kFmt31t: + notHandled = handleFmt31t(cUnit, mir); + break; + case kFmt3rc: + case kFmt35c: + notHandled = handleFmt35c_3rc(cUnit, mir, blockList[i], + labelList); + break; + case kFmt3rms: + case kFmt35ms: + notHandled = handleFmt35ms_3rms(cUnit, mir,blockList[i], + labelList); + break; + case kFmt3inline: + notHandled = handleFmt3inline(cUnit, mir); + break; + case kFmt51l: + notHandled = handleFmt51l(cUnit, mir); + break; + default: + notHandled = true; + break; + } + } + if (notHandled) { + LOGE("%#06x: Opcode 0x%x (%s) / Fmt %d not handled\n", + mir->offset, + dalvikOpCode, getOpcodeName(dalvikOpCode), + dalvikFormat); + dvmAbort(); + break; + } else { + gDvmJit.opHistogram[dalvikOpCode]++; + } + } + } + + /* Handle the codegen in predefined order */ + for (i = 0; i < CHAINING_CELL_LAST; i++) { + size_t j; + int *blockIdList = (int *) chainingListByType[i].elemList; + + cUnit->numChainingCells[i] = chainingListByType[i].numUsed; + + /* No chaining cells of this type */ + if (cUnit->numChainingCells[i] == 0) + continue; + + /* Record the first LIR for a new type of chaining cell */ + cUnit->firstChainingLIR[i] = (LIR *) &labelList[blockIdList[0]]; + + for (j = 0; j < chainingListByType[i].numUsed; j++) { + int blockId = blockIdList[j]; + + /* Align this chaining cell first */ + newLIR0(cUnit, ARMV5TE_PSEUDO_ALIGN4); + + /* Insert the pseudo chaining instruction */ + dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]); + + + switch (blockList[blockId]->blockType) { + case CHAINING_CELL_GENERIC: + handleGenericChainingCell(cUnit, + blockList[blockId]->startOffset); + break; + case CHAINING_CELL_INVOKE: + handleInvokeChainingCell(cUnit, + blockList[blockId]->containingMethod); + break; + case CHAINING_CELL_POST_INVOKE: + handlePostInvokeChainingCell(cUnit, + blockList[blockId]->startOffset); + break; + default: + dvmAbort(); + break; + } + } + } +} + +/* Accept the work and start compiling */ +void *dvmCompilerDoWork(CompilerWorkOrder *work) +{ + void *res; + + if (gDvmJit.codeCacheFull) { + return NULL; + } + + switch (work->kind) { + case kWorkOrderMethod: + res = dvmCompileMethod(work->info); + break; + case kWorkOrderTrace: + res = dvmCompileTrace(work->info); + break; + default: + res = NULL; + dvmAbort(); + } + return res; +} + +/* Architecture-specific initializations and checks go here */ +bool dvmCompilerArchInit(void) +{ + /* First, declare dvmCompiler_TEMPLATE_XXX for each template */ +#define JIT_TEMPLATE(X) extern void dvmCompiler_TEMPLATE_##X(); +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + + int i = 0; + extern void dvmCompilerTemplateStart(void); + + /* + * Then, populate the templateEntryOffsets array with the offsets from the + * the dvmCompilerTemplateStart symbol for each template. + */ +#define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \ + (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart; +#include "../../template/armv5te/TemplateOpList.h" +#undef JIT_TEMPLATE + + /* Codegen-specific assumptions */ + assert(offsetof(ClassObject, vtable) < 128 && + (offsetof(ClassObject, vtable) & 0x3) == 0); + assert(offsetof(ArrayObject, length) < 128 && + (offsetof(ArrayObject, length) & 0x3) == 0); + assert(offsetof(ArrayObject, contents) < 256); + + /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ + assert(sizeof(StackSaveArea) < 236); + + /* + * EA is calculated by doing "Rn + imm5 << 2", and there are 5 entry points + * that codegen may access, make sure that the offset from the top of the + * struct is less than 108. + */ + assert(offsetof(InterpState, jitToInterpEntries) < 108); + return true; +} + +/* Architectural-specific debugging helpers go here */ +void dvmCompilerArchDump(void) +{ + /* Print compiled opcode in this VM instance */ + int i, start, streak; + char buf[1024]; + + streak = i = 0; + buf[0] = 0; + while (opcodeCoverage[i] == 0 && i < 256) { + i++; + } + if (i == 256) { + return; + } + for (start = i++, streak = 1; i < 256; i++) { + if (opcodeCoverage[i]) { + streak++; + } else { + if (streak == 1) { + sprintf(buf+strlen(buf), "%x,", start); + } else { + sprintf(buf+strlen(buf), "%x-%x,", start, start + streak - 1); + } + streak = 0; + while (opcodeCoverage[i] == 0 && i < 256) { + i++; + } + if (i < 256) { + streak = 1; + start = i; + } + } + } + if (streak) { + if (streak == 1) { + sprintf(buf+strlen(buf), "%x", start); + } else { + sprintf(buf+strlen(buf), "%x-%x", start, start + streak - 1); + } + } + if (strlen(buf)) { + LOGD("dalvik.vm.jitop = %s", buf); + } +} diff --git a/vm/compiler/template/Makefile-template b/vm/compiler/template/Makefile-template new file mode 100644 index 000000000..920318335 --- /dev/null +++ b/vm/compiler/template/Makefile-template @@ -0,0 +1,49 @@ +# Copyright (C) 2008 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. + +# +# Makefile for the Dalvik modular interpreter. This is not currently +# integrated into the build system. +# + +SHELL := /bin/sh + +# Build system has TARGET_ARCH=arm, but we need the exact architecture. +# The base assumption for an ARM platform is ARMv5TE, but we may want to +# support older ARMv4 devices, or use special features from ARMv6 or VFP. +# The simulator build is "desktop". +# +# To generate sources for all targets: +# for arch in desktop armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-mterp; done +# +#TARGET_ARCH_EXT := armv5te + +OUTPUT_DIR := out + +# Accumulate all possible dependencies for the generated files in a very +# conservative fashion. If it's not one of the generated files in "out", +# assume it's a dependency. +SOURCE_DEPS := \ + $(shell find . -path ./$(OUTPUT_DIR) -prune -o -type f -print) + +# Source files generated by the script. There's always one C and one +# assembly file, though in practice one or the other could be empty. +GEN_SOURCES := \ + $(OUTPUT_DIR)/CompilerTemplateAsm-$(TARGET_ARCH_EXT).S + +target: $(GEN_SOURCES) + +$(GEN_SOURCES): $(SOURCE_DEPS) + @mkdir -p out + ./gen-template.py $(TARGET_ARCH_EXT) $(OUTPUT_DIR) diff --git a/vm/compiler/template/README.txt b/vm/compiler/template/README.txt new file mode 100644 index 000000000..fced412e9 --- /dev/null +++ b/vm/compiler/template/README.txt @@ -0,0 +1 @@ +See README.txt under dalvik/vm/mterp for details. diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S new file mode 100644 index 000000000..f18f6d3ae --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S @@ -0,0 +1 @@ +%include "armv5te/TEMPLATE_CMPL_DOUBLE.S" { "naninst":"mov r0, #1" } diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S new file mode 100644 index 000000000..02887e57d --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S @@ -0,0 +1 @@ +%include "armv5te/TEMPLATE_CMPL_FLOAT.S" { "naninst":"mov r0, #1" } diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S new file mode 100644 index 000000000..dfafd2c2d --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S @@ -0,0 +1,39 @@ +%default { "naninst":"mvn r0, #0" } + /* + * For the JIT: incoming arguments are pointers to the arguments in r0/r1 + * result in r0 + * + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into r1 depending + * on what value we'd like to return when one of the operands is NaN. + * + * See OP_CMPL_FLOAT for an explanation. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + mov r4, lr @ save return address + mov r9, r0 @ save copy of &arg1 + mov r10, r1 @ save copy of &arg2 + ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1 + ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1 + LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple" + bhi .L${opcode}_gt_or_nan @ C set and Z clear, disambiguate + mvncc r0, #0 @ (less than) r1<- -1 + moveq r0, #0 @ (equal) r1<- 0, trumps less than + bx r4 + + @ Test for NaN with a second comparison. EABI forbids testing bit + @ patterns, and we can't represent 0x7fc00000 in immediate form, so + @ make the library call. +.L${opcode}_gt_or_nan: + ldmia r10, {r0-r1} @ reverse order + ldmia r9, {r2-r3} + LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if < + movcc r0, #1 @ (greater than) r1<- 1 + bxcc r4 + $naninst @ r1<- 1 or -1 for NaN + bx r4 + diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S new file mode 100644 index 000000000..31d4cd82b --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S @@ -0,0 +1,56 @@ +%default { "naninst":"mvn r0, #0" } + /* + * For the JIT: incoming arguments in r0, r1 + * result in r0 + * + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into r1 depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1,1}; // one or both operands was NaN + * + * The straightforward implementation requires 3 calls to functions + * that return a result in r0. We can do it with two calls if our + * EABI library supports __aeabi_cfcmple (only one if we want to check + * for NaN directly): + * check x <= y + * if <, return -1 + * if ==, return 0 + * check y <= x + * if <, return 1 + * return {-1,1} + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + mov r4, lr @ save return address + mov r9, r0 @ Save copies - we may need to redo + mov r10, r1 + LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq + bhi .L${opcode}_gt_or_nan @ C set and Z clear, disambiguate + mvncc r0, #0 @ (less than) r0<- -1 + moveq r0, #0 @ (equal) r0<- 0, trumps less than + bx r4 + @ Test for NaN with a second comparison. EABI forbids testing bit + @ patterns, and we can't represent 0x7fc00000 in immediate form, so + @ make the library call. +.L${opcode}_gt_or_nan: + mov r1, r9 @ reverse order + mov r0, r10 + LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if < + movcc r0, #1 @ (greater than) r1<- 1 + bxcc r4 + $naninst @ r1<- 1 or -1 for NaN + bx r4 + + diff --git a/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S new file mode 100644 index 000000000..5f1e16b3c --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S @@ -0,0 +1,34 @@ + /* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + * + * We load the full values with LDM, but in practice many values could + * be resolved by only looking at the high word. This could be made + * faster or slower by splitting the LDM into a pair of LDRs. + * + * If we just wanted to set condition flags, we could do this: + * subs ip, r0, r2 + * sbcs ip, r1, r3 + * subeqs ip, r0, r2 + * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific + * integer value, which we can do with 2 conditional mov/mvn instructions + * (set 1, set -1; if they're equal we already have 0 in ip), giving + * us a constant 5-cycle path plus a branch at the end to the + * instruction epilogue code. The multi-compare approach below needs + * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch + * in the worst case (the 64-bit values are equal). + */ + /* cmp-long vAA, vBB, vCC */ + cmp r1, r3 @ compare (vBB+1, vCC+1) + blt .L${opcode}_less @ signed compare on high part + bgt .L${opcode}_greater + subs r0, r0, r2 @ r0<- r0 - r2 + bxeq lr + bhi .L${opcode}_greater @ unsigned compare on low part +.L${opcode}_less: + mvn r0, #0 @ r0<- -1 + bx lr +.L${opcode}_greater: + mov r0, #1 @ r0<- 1 + bx lr + diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S new file mode 100644 index 000000000..6994f269f --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S @@ -0,0 +1,54 @@ + /* + * For monomorphic callsite, setup the Dalvik frame and return to the + * Thumb code through the link register to transfer control to the callee + * method through a dedicated chaining cell. + */ + @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite + ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize + ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize + ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd + ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount + add r3, r1, #1 @ Thumb addr is odd + SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area + sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize) + SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area + add r12, lr, #2 @ setup the punt-to-interp address + sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize) + ldr r8, [r8] @ r3<- suspendCount (int) + cmp r10, r9 @ bottom < interpStackEnd? + bxlt r12 @ return to raise stack overflow excep. + @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite + ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz + ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags + str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)] + str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)] + ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns + + + @ set up newSaveArea + str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)] + str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)] + str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)] + cmp r8, #0 @ suspendCount != 0 + bxne r12 @ bail to the interpreter + tst r10, #ACC_NATIVE + bne .LinvokeNative + /* + * If we want to punt to the interpreter for native call, swap the bne with + * the following + * bxne r12 + */ + + + ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex + ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self + + @ Update "glue" values for the new method + str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall + str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ... + mov rFP, r1 @ fp = newFp + str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp + + bx lr @ return to the callee-chaining cell + + diff --git a/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S new file mode 100644 index 000000000..003459dc8 --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S @@ -0,0 +1,53 @@ + /* + * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC + * into rPC then jump to dvmJitToInterpNoChain to dispatch the + * runtime-resolved callee. + */ + @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite + ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize + ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize + ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd + ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount + add r3, r1, #1 @ Thumb addr is odd + SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area + sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize) + SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area + sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize) + ldr r8, [r8] @ r3<- suspendCount (int) + cmp r10, r9 @ bottom < interpStackEnd? + bxlt lr @ return to raise stack overflow excep. + @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite + ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz + ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags + str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)] + str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)] + ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns + + + @ set up newSaveArea + str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)] + str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)] + str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)] + cmp r8, #0 @ suspendCount != 0 + bxne lr @ bail to the interpreter + tst r10, #ACC_NATIVE + bne .LinvokeNative + /* + * If we want to punt to the interpreter for native call, swap the bne with + * the following + * bxne lr + */ + + + ldr r10, .LdvmJitToInterpNoChain + ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex + ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self + + @ Update "glue" values for the new method + str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall + str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ... + mov rFP, r1 @ fp = newFp + str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp + + @ Start executing the callee + mov pc, r10 @ dvmJitToInterpNoChain diff --git a/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S new file mode 100644 index 000000000..8a9b11574 --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S @@ -0,0 +1,28 @@ + /* + * Signed 64-bit integer multiply. + * + * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1 + * + * Consider WXxYZ (r1r0 x r3r2) with a long multiply: + * WX + * x YZ + * -------- + * ZW ZX + * YW YX + * + * The low word of the result holds ZX, the high word holds + * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because + * it doesn't fit in the low 64 bits. + * + * Unlike most ARM math operations, multiply instructions have + * restrictions on using the same register more than once (Rd and Rm + * cannot be the same). + */ + /* mul-long vAA, vBB, vCC */ + mul ip, r2, r1 @ ip<- ZxW + umull r9, r10, r2, r0 @ r9/r10 <- ZxX + mla r2, r0, r3, ip @ r2<- YxX + (ZxW) + add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX)) + mov r0,r9 + mov r1,r10 + bx lr diff --git a/vm/compiler/template/armv5te/TEMPLATE_RETURN.S b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S new file mode 100644 index 000000000..f0a4623a0 --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_RETURN.S @@ -0,0 +1,38 @@ + /* + * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX. + * If the stored value in returnAddr + * is non-zero, the caller is compiled by the JIT thus return to the + * address in the code cache following the invoke instruction. Otherwise + * return to the special dvmJitToInterpNoChain entry point. + */ + SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old) + ldr r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame + ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount + ldr rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc + ldr r9, [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret + ldr r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)] + @ r2<- method we're returning to + ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self + cmp r2, #0 @ break frame? + beq 1f @ bail to interpreter + ldr r0, .LdvmJitToInterpNoChain @ defined in footer.S + mov rFP, r10 @ publish new FP + ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz + ldr r8, [r8] @ r8<- suspendCount + + str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method + ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex + str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp + add rPC, rPC, #6 @ publish new rPC (advance 6 bytes) + str r1, [rGLUE, #offGlue_methodClassDex] + cmp r8, #0 @ check the suspendCount + movne r9, #0 @ clear the chaining cell address + cmp r9, #0 @ chaining cell exists? + blxne r9 @ jump to the chaining cell + mov pc, r0 @ callsite is interpreted +1: + stmia rGLUE, {rPC, rFP} @ SAVE_PC_FP_TO_GLUE() + ldr r2, .LdvmMterpStdBail @ defined in footer.S + mov r1, #0 @ changeInterp = false + mov r0, rGLUE @ Expecting rGLUE in r0 + blx r2 @ exit the interpreter diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S new file mode 100644 index 000000000..532f8a47a --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S @@ -0,0 +1,15 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low + * 6 bits. + */ + /* shl-long vAA, vBB, vCC */ + and r2, r2, #63 @ r2<- r2 & 0x3f + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r0, r0, asl r2 @ r0<- r0 << r2 + bx lr diff --git a/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S new file mode 100644 index 000000000..ca7545a18 --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S @@ -0,0 +1,16 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low + * 6 bits. + */ + /* shr-long vAA, vBB, vCC */ + and r2, r2, #63 @ r0<- r0 & 0x3f + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r1, r1, asr r2 @ r1<- r1 >> r2 + bx lr + diff --git a/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S b/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S new file mode 100644 index 000000000..d7c71d9df --- /dev/null +++ b/vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S @@ -0,0 +1,16 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low + * 6 bits. + */ + /* ushr-long vAA, vBB, vCC */ + and r2, r2, #63 @ r0<- r0 & 0x3f + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + bx lr + diff --git a/vm/compiler/template/armv5te/TemplateOpList.h b/vm/compiler/template/armv5te/TemplateOpList.h new file mode 100644 index 000000000..6428ccf12 --- /dev/null +++ b/vm/compiler/template/armv5te/TemplateOpList.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 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. + */ + +/* + * Dalvik opcode list that uses additional templates to complete JIT execution. + */ +#ifndef JIT_TEMPLATE +#define JIT_TEMPLATE(X) +#endif + +JIT_TEMPLATE(CMP_LONG) +JIT_TEMPLATE(RETURN) +JIT_TEMPLATE(INVOKE_METHOD_NO_OPT) +JIT_TEMPLATE(INVOKE_METHOD_CHAIN) +JIT_TEMPLATE(CMPG_DOUBLE) +JIT_TEMPLATE(CMPL_DOUBLE) +JIT_TEMPLATE(CMPG_FLOAT) +JIT_TEMPLATE(CMPL_FLOAT) +JIT_TEMPLATE(MUL_LONG) +JIT_TEMPLATE(SHL_LONG) +JIT_TEMPLATE(SHR_LONG) +JIT_TEMPLATE(USHR_LONG) diff --git a/vm/compiler/template/armv5te/footer.S b/vm/compiler/template/armv5te/footer.S new file mode 100644 index 000000000..e961e298b --- /dev/null +++ b/vm/compiler/template/armv5te/footer.S @@ -0,0 +1,58 @@ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 +.LinvokeNative: + @ Prep for the native call + @ r1 = newFP, r0 = methodToCall + ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self + ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext + str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp + str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)] + @ newFp->localRefTop=refNext + mov r9, r3 @ r9<- glue->self (preserve) + SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area + + mov r2, r0 @ r2<- methodToCall + mov r0, r1 @ r0<- newFP + add r1, rGLUE, #offGlue_retval @ r1<- &retval + + LDR_PC_LR "[r2, #offMethod_nativeFunc]" + + @ native return; r9=self, r10=newSaveArea + @ equivalent to dvmPopJniLocals + ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret + ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop + ldr r1, [r9, #offThread_exception] @ check for exception + str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp + cmp r1, #0 @ null? + str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0 + bne .LhandleException @ no, handle exception + bx r2 + +/* FIXME - untested */ +.LhandleException: + ldr rIBASE, .LdvmAsmInstructionStart + ldr rPC, [r10, #offStackSaveArea_savedPc] @ reload rPC + b dvmMterpCommonExceptionThrown + + .align 2 +.LdvmAsmInstructionStart: + .word dvmAsmInstructionStart +.LdvmJitToInterpNoChain: + .word dvmJitToInterpNoChain +.LdvmMterpStdBail: + .word dvmMterpStdBail +.L__aeabi_cdcmple: + .word __aeabi_cdcmple +.L__aeabi_cfcmple: + .word __aeabi_cfcmple + + .global dmvCompilerTemplateEnd +dmvCompilerTemplateEnd: + +#endif /* WITH_JIT */ diff --git a/vm/compiler/template/armv5te/header.S b/vm/compiler/template/armv5te/header.S new file mode 100644 index 000000000..65daf8d73 --- /dev/null +++ b/vm/compiler/template/armv5te/header.S @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 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. + */ + +#if defined(WITH_JIT) + +/* + * ARMv5 definitions and declarations. + */ + +/* +ARM EABI general notes: + +r0-r3 hold first 4 args to a method; they are not preserved across method calls +r4-r8 are available for general use +r9 is given special treatment in some situations, but not for us +r10 (sl) seems to be generally available +r11 (fp) is used by gcc (unless -fomit-frame-pointer is set) +r12 (ip) is scratch -- not preserved across method calls +r13 (sp) should be managed carefully in case a signal arrives +r14 (lr) must be preserved +r15 (pc) can be tinkered with directly + +r0 holds returns of <= 4 bytes +r0-r1 hold returns of 8 bytes, low word in r0 + +Callee must save/restore r4+ (except r12) if it modifies them. + +Stack is "full descending". Only the arguments that don't fit in the first 4 +registers are placed on the stack. "sp" points at the first stacked argument +(i.e. the 5th arg). + +VFP: single-precision results in s0, double-precision results in d0. + +In the EABI, "sp" must be 64-bit aligned on entry to a function, and any +64-bit quantities (long long, double) must be 64-bit aligned. +*/ + +/* +JIT and ARM notes: + +The following registers have fixed assignments: + + reg nick purpose + r5 rFP interpreted frame pointer, used for accessing locals and args + r6 rGLUE MterpGlue pointer + +The following registers have fixed assignments in mterp but are scratch +registers in compiled code + + reg nick purpose + r4 rPC interpreted program counter, used for fetching instructions + r7 rIBASE interpreted instruction base pointer, used for computed goto + r8 rINST first 16-bit code unit of current instruction + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* single-purpose registers, given names for clarity */ +#define rPC r4 +#define rFP r5 +#define rGLUE r6 +#define rIBASE r7 +#define rINST r8 + +/* + * Given a frame pointer, find the stack save area. + * + * In C this is "((StackSaveArea*)(_fp) -1)". + */ +#define SAVEAREA_FROM_FP(_reg, _fpreg) \ + sub _reg, _fpreg, #sizeofStackSaveArea + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "../../../mterp/common/asm-constants.h" + diff --git a/vm/compiler/template/armv5te/platform.S b/vm/compiler/template/armv5te/platform.S new file mode 100644 index 000000000..b960a9364 --- /dev/null +++ b/vm/compiler/template/armv5te/platform.S @@ -0,0 +1,16 @@ +/* + * =========================================================================== + * CPU-version-specific defines + * =========================================================================== + */ + +/* + * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5. + * Jump to subroutine. + * + * May modify IP and LR. + */ +.macro LDR_PC_LR source + mov lr, pc + ldr pc, \source +.endm diff --git a/vm/compiler/template/config-armv5te b/vm/compiler/template/config-armv5te new file mode 100644 index 000000000..668df1b92 --- /dev/null +++ b/vm/compiler/template/config-armv5te @@ -0,0 +1,45 @@ +# Copyright (C) 2009 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. + +# +# Configuration for ARMv5TE architecture targets. +# + +# file header and basic definitions +#import c/header.c +import armv5te/header.S + +# C pre-processor defines for stub C instructions +#import cstubs/stubdefs.c + +# highly-platform-specific defs +import armv5te/platform.S + +# common defs for the C helpers; include this before the instruction handlers +#import c/opcommon.c + +# opcode list; argument to op-start is default directory +op-start armv5te + +op-end + +# "helper" code for C; include if you use any of the C stubs (this generates +# object code, so it's normally excluded) +##import c/gotoTargets.c + +# end of defs; include this when cstubs/stubdefs.c is included +#import cstubs/enddefs.c + +# common subroutines for asm +import armv5te/footer.S diff --git a/vm/compiler/template/gen-template.py b/vm/compiler/template/gen-template.py new file mode 100755 index 000000000..8a1ba0cb9 --- /dev/null +++ b/vm/compiler/template/gen-template.py @@ -0,0 +1,422 @@ +#!/usr/bin/env python +# +# Copyright (C) 2007 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. + +# +# Using instructions from an architecture-specific config file, generate C +# and assembly source files for the Dalvik JIT. +# + +import sys, string, re, time +from string import Template + +interp_defs_file = "TemplateOpList.h" # need opcode list + +handler_size_bits = -1000 +handler_size_bytes = -1000 +in_op_start = 0 # 0=not started, 1=started, 2=ended +default_op_dir = None +opcode_locations = {} +asm_stub_text = [] +label_prefix = ".L" # use ".L" to hide labels from gdb + + +# Exception class. +class DataParseError(SyntaxError): + "Failure when parsing data file" + +# +# Set any omnipresent substitution values. +# +def getGlobalSubDict(): + return { "handler_size_bits":handler_size_bits, + "handler_size_bytes":handler_size_bytes } + +# +# Parse arch config file -- +# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to +# log2(handler_size_bytes). Throws an exception if "bytes" is not a power +# of two. +# +def setHandlerSize(tokens): + global handler_size_bits, handler_size_bytes + if len(tokens) != 2: + raise DataParseError("handler-size requires one argument") + if handler_size_bits != -1000: + raise DataParseError("handler-size may only be set once") + + # compute log2(n), and make sure n is a power of 2 + handler_size_bytes = bytes = int(tokens[1]) + bits = -1 + while bytes > 0: + bytes //= 2 # halve with truncating division + bits += 1 + + if handler_size_bytes == 0 or handler_size_bytes != (1 << bits): + raise DataParseError("handler-size (%d) must be power of 2 and > 0" \ + % orig_bytes) + handler_size_bits = bits + +# +# Parse arch config file -- +# Copy a file in to the C or asm output file. +# +def importFile(tokens): + if len(tokens) != 2: + raise DataParseError("import requires one argument") + source = tokens[1] + if source.endswith(".S"): + appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None) + else: + raise DataParseError("don't know how to import %s (expecting .c/.S)" + % source) + +# +# Parse arch config file -- +# Copy a file in to the C or asm output file. +# +def setAsmStub(tokens): + global asm_stub_text + if len(tokens) != 2: + raise DataParseError("import requires one argument") + try: + stub_fp = open(tokens[1]) + asm_stub_text = stub_fp.readlines() + except IOError, err: + stub_fp.close() + raise DataParseError("unable to load asm-stub: %s" % str(err)) + stub_fp.close() + +# +# Parse arch config file -- +# Start of opcode list. +# +def opStart(tokens): + global in_op_start + global default_op_dir + if len(tokens) != 2: + raise DataParseError("opStart takes a directory name argument") + if in_op_start != 0: + raise DataParseError("opStart can only be specified once") + default_op_dir = tokens[1] + in_op_start = 1 + +# +# Parse arch config file -- +# Set location of a single opcode's source file. +# +def opEntry(tokens): + #global opcode_locations + if len(tokens) != 3: + raise DataParseError("op requires exactly two arguments") + if in_op_start != 1: + raise DataParseError("op statements must be between opStart/opEnd") + try: + index = opcodes.index(tokens[1]) + except ValueError: + raise DataParseError("unknown opcode %s" % tokens[1]) + opcode_locations[tokens[1]] = tokens[2] + +# +# Parse arch config file -- +# End of opcode list; emit instruction blocks. +# +def opEnd(tokens): + global in_op_start + if len(tokens) != 1: + raise DataParseError("opEnd takes no arguments") + if in_op_start != 1: + raise DataParseError("opEnd must follow opStart, and only appear once") + in_op_start = 2 + + loadAndEmitOpcodes() + + +# +# Extract an ordered list of instructions from the VM sources. We use the +# "goto table" definition macro, which has exactly 256 entries. +# +def getOpcodeList(): + opcodes = [] + opcode_fp = open("%s/%s" % (target_arch, interp_defs_file)) + opcode_re = re.compile(r"^JIT_TEMPLATE\((\w+)\)", re.DOTALL) + for line in opcode_fp: + match = opcode_re.match(line) + if not match: + continue + opcodes.append("TEMPLATE_" + match.group(1)) + opcode_fp.close() + + return opcodes + + +# +# Load and emit opcodes for all 256 instructions. +# +def loadAndEmitOpcodes(): + sister_list = [] + + # point dvmAsmInstructionStart at the first handler or stub + asm_fp.write("\n .global dvmCompilerTemplateStart\n") + asm_fp.write(" .type dvmCompilerTemplateStart, %function\n") + asm_fp.write(" .text\n\n") + asm_fp.write("dvmCompilerTemplateStart:\n\n") + + for i in xrange(len(opcodes)): + op = opcodes[i] + + if opcode_locations.has_key(op): + location = opcode_locations[op] + else: + location = default_op_dir + + loadAndEmitAsm(location, i, sister_list) + + # Use variable sized handlers now + # asm_fp.write("\n .balign %d\n" % handler_size_bytes) + asm_fp.write(" .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart\n") + +# +# Load an assembly fragment and emit it. +# +def loadAndEmitAsm(location, opindex, sister_list): + op = opcodes[opindex] + source = "%s/%s.S" % (location, op) + dict = getGlobalSubDict() + dict.update({ "opcode":op, "opnum":opindex }) + print " emit %s --> asm" % source + + emitAsmHeader(asm_fp, dict) + appendSourceFile(source, dict, asm_fp, sister_list) + +# +# Output the alignment directive and label for an assembly piece. +# +def emitAsmHeader(outfp, dict): + outfp.write("/* ------------------------------ */\n") + # The alignment directive ensures that the handler occupies + # at least the correct amount of space. We don't try to deal + # with overflow here. + outfp.write(" .balign 4\n") + # Emit a label so that gdb will say the right thing. We prepend an + # underscore so the symbol name doesn't clash with the OpCode enum. + template_name = "dvmCompiler_%(opcode)s" % dict + outfp.write(" .global %s\n" % template_name); + outfp.write("%s:\n" % template_name); + +# +# Output a generic instruction stub that updates the "glue" struct and +# calls the C implementation. +# +def emitAsmStub(outfp, dict): + emitAsmHeader(outfp, dict) + for line in asm_stub_text: + templ = Template(line) + outfp.write(templ.substitute(dict)) + +# +# Append the file specified by "source" to the open "outfp". Each line will +# be template-replaced using the substitution dictionary "dict". +# +# If the first line of the file starts with "%" it is taken as a directive. +# A "%include" line contains a filename and, optionally, a Python-style +# dictionary declaration with substitution strings. (This is implemented +# with recursion.) +# +# If "sister_list" is provided, and we find a line that contains only "&", +# all subsequent lines from the file will be appended to sister_list instead +# of copied to the output. +# +# This may modify "dict". +# +def appendSourceFile(source, dict, outfp, sister_list): + outfp.write("/* File: %s */\n" % source) + infp = open(source, "r") + in_sister = False + for line in infp: + if line.startswith("%include"): + # Parse the "include" line + tokens = line.strip().split(' ', 2) + if len(tokens) < 2: + raise DataParseError("malformed %%include in %s" % source) + + alt_source = tokens[1].strip("\"") + if alt_source == source: + raise DataParseError("self-referential %%include in %s" + % source) + + new_dict = dict.copy() + if len(tokens) == 3: + new_dict.update(eval(tokens[2])) + #print " including src=%s dict=%s" % (alt_source, new_dict) + appendSourceFile(alt_source, new_dict, outfp, sister_list) + continue + + elif line.startswith("%default"): + # copy keywords into dictionary + tokens = line.strip().split(' ', 1) + if len(tokens) < 2: + raise DataParseError("malformed %%default in %s" % source) + defaultValues = eval(tokens[1]) + for entry in defaultValues: + dict.setdefault(entry, defaultValues[entry]) + continue + + elif line.startswith("%verify"): + # more to come, someday + continue + + elif line.startswith("%break") and sister_list != None: + # allow more than one %break, ignoring all following the first + if not in_sister: + in_sister = True + sister_list.append("\n/* continuation for %(opcode)s */\n"%dict) + continue + + # perform keyword substitution if a dictionary was provided + if dict != None: + templ = Template(line) + try: + subline = templ.substitute(dict) + except KeyError, err: + raise DataParseError("keyword substitution failed in %s: %s" + % (source, str(err))) + except: + print "ERROR: substitution failed: " + line + raise + else: + subline = line + + # write output to appropriate file + if in_sister: + sister_list.append(subline) + else: + outfp.write(subline) + outfp.write("\n") + infp.close() + +# +# Emit a C-style section header comment. +# +def emitSectionComment(str, fp): + equals = "========================================" \ + "===================================" + + fp.write("\n/*\n * %s\n * %s\n * %s\n */\n" % + (equals, str, equals)) + + +# +# =========================================================================== +# "main" code +# + +# +# Check args. +# +if len(sys.argv) != 3: + print "Usage: %s target-arch output-dir" % sys.argv[0] + sys.exit(2) + +target_arch = sys.argv[1] +output_dir = sys.argv[2] + +# +# Extract opcode list. +# +opcodes = getOpcodeList() +#for op in opcodes: +# print " %s" % op + +# +# Open config file. +# +try: + config_fp = open("config-%s" % target_arch) +except: + print "Unable to open config file 'config-%s'" % target_arch + sys.exit(1) + +# +# Open and prepare output files. +# +try: + asm_fp = open("%s/CompilerTemplateAsm-%s.S" % (output_dir, target_arch), "w") +except: + print "Unable to open output files" + print "Make sure directory '%s' exists and existing files are writable" \ + % output_dir + # Ideally we'd remove the files to avoid confusing "make", but if they + # failed to open we probably won't be able to remove them either. + sys.exit(1) + +print "Generating %s" % (asm_fp.name) + +file_header = """/* + * This file was generated automatically by gen-template.py for '%s'. + * + * --> DO NOT EDIT <-- + */ + +""" % (target_arch) + +asm_fp.write(file_header) + +# +# Process the config file. +# +failed = False +try: + for line in config_fp: + line = line.strip() # remove CRLF, leading spaces + tokens = line.split(' ') # tokenize + #print "%d: %s" % (len(tokens), tokens) + if len(tokens[0]) == 0: + #print " blank" + pass + elif tokens[0][0] == '#': + #print " comment" + pass + else: + if tokens[0] == "handler-size": + setHandlerSize(tokens) + elif tokens[0] == "import": + importFile(tokens) + elif tokens[0] == "asm-stub": + setAsmStub(tokens) + elif tokens[0] == "op-start": + opStart(tokens) + elif tokens[0] == "op-end": + opEnd(tokens) + elif tokens[0] == "op": + opEntry(tokens) + else: + raise DataParseError, "unrecognized command '%s'" % tokens[0] +except DataParseError, err: + print "Failed: " + str(err) + # TODO: remove output files so "make" doesn't get confused + failed = True + asm_fp.close() + c_fp = asm_fp = None + +config_fp.close() + +# +# Done! +# +if asm_fp: + asm_fp.close() + +sys.exit(failed) diff --git a/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S new file mode 100644 index 000000000..59aa7906f --- /dev/null +++ b/vm/compiler/template/out/CompilerTemplateAsm-armv5te.S @@ -0,0 +1,703 @@ +/* + * This file was generated automatically by gen-template.py for 'armv5te'. + * + * --> DO NOT EDIT <-- + */ + +/* File: armv5te/header.S */ +/* + * Copyright (C) 2008 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. + */ + +#if defined(WITH_JIT) + +/* + * ARMv5 definitions and declarations. + */ + +/* +ARM EABI general notes: + +r0-r3 hold first 4 args to a method; they are not preserved across method calls +r4-r8 are available for general use +r9 is given special treatment in some situations, but not for us +r10 (sl) seems to be generally available +r11 (fp) is used by gcc (unless -fomit-frame-pointer is set) +r12 (ip) is scratch -- not preserved across method calls +r13 (sp) should be managed carefully in case a signal arrives +r14 (lr) must be preserved +r15 (pc) can be tinkered with directly + +r0 holds returns of <= 4 bytes +r0-r1 hold returns of 8 bytes, low word in r0 + +Callee must save/restore r4+ (except r12) if it modifies them. + +Stack is "full descending". Only the arguments that don't fit in the first 4 +registers are placed on the stack. "sp" points at the first stacked argument +(i.e. the 5th arg). + +VFP: single-precision results in s0, double-precision results in d0. + +In the EABI, "sp" must be 64-bit aligned on entry to a function, and any +64-bit quantities (long long, double) must be 64-bit aligned. +*/ + +/* +JIT and ARM notes: + +The following registers have fixed assignments: + + reg nick purpose + r5 rFP interpreted frame pointer, used for accessing locals and args + r6 rGLUE MterpGlue pointer + +The following registers have fixed assignments in mterp but are scratch +registers in compiled code + + reg nick purpose + r4 rPC interpreted program counter, used for fetching instructions + r7 rIBASE interpreted instruction base pointer, used for computed goto + r8 rINST first 16-bit code unit of current instruction + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* single-purpose registers, given names for clarity */ +#define rPC r4 +#define rFP r5 +#define rGLUE r6 +#define rIBASE r7 +#define rINST r8 + +/* + * Given a frame pointer, find the stack save area. + * + * In C this is "((StackSaveArea*)(_fp) -1)". + */ +#define SAVEAREA_FROM_FP(_reg, _fpreg) \ + sub _reg, _fpreg, #sizeofStackSaveArea + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "../../../mterp/common/asm-constants.h" + + +/* File: armv5te/platform.S */ +/* + * =========================================================================== + * CPU-version-specific defines + * =========================================================================== + */ + +/* + * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5. + * Jump to subroutine. + * + * May modify IP and LR. + */ +.macro LDR_PC_LR source + mov lr, pc + ldr pc, \source +.endm + + + .global dvmCompilerTemplateStart + .type dvmCompilerTemplateStart, %function + .text + +dvmCompilerTemplateStart: + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_CMP_LONG +dvmCompiler_TEMPLATE_CMP_LONG: +/* File: armv5te/TEMPLATE_CMP_LONG.S */ + /* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + * + * We load the full values with LDM, but in practice many values could + * be resolved by only looking at the high word. This could be made + * faster or slower by splitting the LDM into a pair of LDRs. + * + * If we just wanted to set condition flags, we could do this: + * subs ip, r0, r2 + * sbcs ip, r1, r3 + * subeqs ip, r0, r2 + * Leaving { <0, 0, >0 } in ip. However, we have to set it to a specific + * integer value, which we can do with 2 conditional mov/mvn instructions + * (set 1, set -1; if they're equal we already have 0 in ip), giving + * us a constant 5-cycle path plus a branch at the end to the + * instruction epilogue code. The multi-compare approach below needs + * 2 or 3 cycles + branch if the high word doesn't match, 6 + branch + * in the worst case (the 64-bit values are equal). + */ + /* cmp-long vAA, vBB, vCC */ + cmp r1, r3 @ compare (vBB+1, vCC+1) + blt .LTEMPLATE_CMP_LONG_less @ signed compare on high part + bgt .LTEMPLATE_CMP_LONG_greater + subs r0, r0, r2 @ r0<- r0 - r2 + bxeq lr + bhi .LTEMPLATE_CMP_LONG_greater @ unsigned compare on low part +.LTEMPLATE_CMP_LONG_less: + mvn r0, #0 @ r0<- -1 + bx lr +.LTEMPLATE_CMP_LONG_greater: + mov r0, #1 @ r0<- 1 + bx lr + + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_RETURN +dvmCompiler_TEMPLATE_RETURN: +/* File: armv5te/TEMPLATE_RETURN.S */ + /* + * Unwind a frame from the Dalvik stack for compiled OP_RETURN_XXX. + * If the stored value in returnAddr + * is non-zero, the caller is compiled by the JIT thus return to the + * address in the code cache following the invoke instruction. Otherwise + * return to the special dvmJitToInterpNoChain entry point. + */ + SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old) + ldr r10, [r0, #offStackSaveArea_prevFrame] @ r10<- saveArea->prevFrame + ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount + ldr rPC, [r0, #offStackSaveArea_savedPc] @ rPC<- saveArea->savedPc + ldr r9, [r0, #offStackSaveArea_returnAddr] @ r9<- chaining cell ret + ldr r2, [r10, #(offStackSaveArea_method - sizeofStackSaveArea)] + @ r2<- method we're returning to + ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self + cmp r2, #0 @ break frame? + beq 1f @ bail to interpreter + ldr r0, .LdvmJitToInterpNoChain @ defined in footer.S + mov rFP, r10 @ publish new FP + ldrne r10, [r2, #offMethod_clazz] @ r10<- method->clazz + ldr r8, [r8] @ r8<- suspendCount + + str r2, [rGLUE, #offGlue_method]@ glue->method = newSave->method + ldr r1, [r10, #offClassObject_pDvmDex] @ r1<- method->clazz->pDvmDex + str rFP, [r3, #offThread_curFrame] @ self->curFrame = fp + add rPC, rPC, #6 @ publish new rPC (advance 6 bytes) + str r1, [rGLUE, #offGlue_methodClassDex] + cmp r8, #0 @ check the suspendCount + movne r9, #0 @ clear the chaining cell address + cmp r9, #0 @ chaining cell exists? + blxne r9 @ jump to the chaining cell + mov pc, r0 @ callsite is interpreted +1: + stmia rGLUE, {rPC, rFP} @ SAVE_PC_FP_TO_GLUE() + ldr r2, .LdvmMterpStdBail @ defined in footer.S + mov r1, #0 @ changeInterp = false + mov r0, rGLUE @ Expecting rGLUE in r0 + blx r2 @ exit the interpreter + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT +dvmCompiler_TEMPLATE_INVOKE_METHOD_NO_OPT: +/* File: armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S */ + /* + * For polymorphic callsites - setup the Dalvik frame and load Dalvik PC + * into rPC then jump to dvmJitToInterpNoChain to dispatch the + * runtime-resolved callee. + */ + @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite + ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize + ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize + ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd + ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount + add r3, r1, #1 @ Thumb addr is odd + SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area + sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize) + SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area + sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize) + ldr r8, [r8] @ r3<- suspendCount (int) + cmp r10, r9 @ bottom < interpStackEnd? + bxlt lr @ return to raise stack overflow excep. + @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite + ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz + ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags + str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)] + str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)] + ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns + + + @ set up newSaveArea + str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)] + str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)] + str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)] + cmp r8, #0 @ suspendCount != 0 + bxne lr @ bail to the interpreter + tst r10, #ACC_NATIVE + bne .LinvokeNative + /* + * If we want to punt to the interpreter for native call, swap the bne with + * the following + * bxne lr + */ + + + ldr r10, .LdvmJitToInterpNoChain + ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex + ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self + + @ Update "glue" values for the new method + str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall + str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ... + mov rFP, r1 @ fp = newFp + str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp + + @ Start executing the callee + mov pc, r10 @ dvmJitToInterpNoChain + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN +dvmCompiler_TEMPLATE_INVOKE_METHOD_CHAIN: +/* File: armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S */ + /* + * For monomorphic callsite, setup the Dalvik frame and return to the + * Thumb code through the link register to transfer control to the callee + * method through a dedicated chaining cell. + */ + @ r0 = methodToCall, r1 = returnCell, rPC = dalvikCallsite + ldrh r7, [r0, #offMethod_registersSize] @ r7<- methodToCall->regsSize + ldrh r2, [r0, #offMethod_outsSize] @ r2<- methodToCall->outsSize + ldr r9, [rGLUE, #offGlue_interpStackEnd] @ r9<- interpStackEnd + ldr r8, [rGLUE, #offGlue_pSelfSuspendCount] @ r8<- &suspendCount + add r3, r1, #1 @ Thumb addr is odd + SAVEAREA_FROM_FP(r1, rFP) @ r1<- stack save area + sub r1, r1, r7, lsl #2 @ r1<- newFp (old savearea - regsSize) + SAVEAREA_FROM_FP(r10, r1) @ r10<- stack save area + add r12, lr, #2 @ setup the punt-to-interp address + sub r10, r10, r2, lsl #2 @ r10<- bottom (newsave - outsSize) + ldr r8, [r8] @ r3<- suspendCount (int) + cmp r10, r9 @ bottom < interpStackEnd? + bxlt r12 @ return to raise stack overflow excep. + @ r1 = newFP, r0 = methodToCall, r3 = returnCell, rPC = dalvikCallsite + ldr r9, [r0, #offMethod_clazz] @ r9<- method->clazz + ldr r10, [r0, #offMethod_accessFlags] @ r10<- methodToCall->accessFlags + str rPC, [rFP, #(offStackSaveArea_currentPc - sizeofStackSaveArea)] + str rPC, [r1, #(offStackSaveArea_savedPc - sizeofStackSaveArea)] + ldr rPC, [r0, #offMethod_insns] @ rPC<- methodToCall->insns + + + @ set up newSaveArea + str rFP, [r1, #(offStackSaveArea_prevFrame - sizeofStackSaveArea)] + str r3, [r1, #(offStackSaveArea_returnAddr - sizeofStackSaveArea)] + str r0, [r1, #(offStackSaveArea_method - sizeofStackSaveArea)] + cmp r8, #0 @ suspendCount != 0 + bxne r12 @ bail to the interpreter + tst r10, #ACC_NATIVE + bne .LinvokeNative + /* + * If we want to punt to the interpreter for native call, swap the bne with + * the following + * bxne r12 + */ + + + ldr r3, [r9, #offClassObject_pDvmDex] @ r3<- method->clazz->pDvmDex + ldr r2, [rGLUE, #offGlue_self] @ r2<- glue->self + + @ Update "glue" values for the new method + str r0, [rGLUE, #offGlue_method] @ glue->method = methodToCall + str r3, [rGLUE, #offGlue_methodClassDex] @ glue->methodClassDex = ... + mov rFP, r1 @ fp = newFp + str rFP, [r2, #offThread_curFrame] @ self->curFrame = newFp + + bx lr @ return to the callee-chaining cell + + + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_CMPG_DOUBLE +dvmCompiler_TEMPLATE_CMPG_DOUBLE: +/* File: armv5te/TEMPLATE_CMPG_DOUBLE.S */ +/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */ + /* + * For the JIT: incoming arguments are pointers to the arguments in r0/r1 + * result in r0 + * + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into r1 depending + * on what value we'd like to return when one of the operands is NaN. + * + * See OP_CMPL_FLOAT for an explanation. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + mov r4, lr @ save return address + mov r9, r0 @ save copy of &arg1 + mov r10, r1 @ save copy of &arg2 + ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1 + ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1 + LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple" + bhi .LTEMPLATE_CMPG_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate + mvncc r0, #0 @ (less than) r1<- -1 + moveq r0, #0 @ (equal) r1<- 0, trumps less than + bx r4 + + @ Test for NaN with a second comparison. EABI forbids testing bit + @ patterns, and we can't represent 0x7fc00000 in immediate form, so + @ make the library call. +.LTEMPLATE_CMPG_DOUBLE_gt_or_nan: + ldmia r10, {r0-r1} @ reverse order + ldmia r9, {r2-r3} + LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if < + movcc r0, #1 @ (greater than) r1<- 1 + bxcc r4 + mov r0, #1 @ r1<- 1 or -1 for NaN + bx r4 + + + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_CMPL_DOUBLE +dvmCompiler_TEMPLATE_CMPL_DOUBLE: +/* File: armv5te/TEMPLATE_CMPL_DOUBLE.S */ + /* + * For the JIT: incoming arguments are pointers to the arguments in r0/r1 + * result in r0 + * + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into r1 depending + * on what value we'd like to return when one of the operands is NaN. + * + * See OP_CMPL_FLOAT for an explanation. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + mov r4, lr @ save return address + mov r9, r0 @ save copy of &arg1 + mov r10, r1 @ save copy of &arg2 + ldmia r9, {r0-r1} @ r0/r1<- vBB/vBB+1 + ldmia r10, {r2-r3} @ r2/r3<- vCC/vCC+1 + LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple" + bhi .LTEMPLATE_CMPL_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate + mvncc r0, #0 @ (less than) r1<- -1 + moveq r0, #0 @ (equal) r1<- 0, trumps less than + bx r4 + + @ Test for NaN with a second comparison. EABI forbids testing bit + @ patterns, and we can't represent 0x7fc00000 in immediate form, so + @ make the library call. +.LTEMPLATE_CMPL_DOUBLE_gt_or_nan: + ldmia r10, {r0-r1} @ reverse order + ldmia r9, {r2-r3} + LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if < + movcc r0, #1 @ (greater than) r1<- 1 + bxcc r4 + mvn r0, #0 @ r1<- 1 or -1 for NaN + bx r4 + + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_CMPG_FLOAT +dvmCompiler_TEMPLATE_CMPG_FLOAT: +/* File: armv5te/TEMPLATE_CMPG_FLOAT.S */ +/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */ + /* + * For the JIT: incoming arguments in r0, r1 + * result in r0 + * + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into r1 depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1,1}; // one or both operands was NaN + * + * The straightforward implementation requires 3 calls to functions + * that return a result in r0. We can do it with two calls if our + * EABI library supports __aeabi_cfcmple (only one if we want to check + * for NaN directly): + * check x <= y + * if <, return -1 + * if ==, return 0 + * check y <= x + * if <, return 1 + * return {-1,1} + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + mov r4, lr @ save return address + mov r9, r0 @ Save copies - we may need to redo + mov r10, r1 + LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq + bhi .LTEMPLATE_CMPG_FLOAT_gt_or_nan @ C set and Z clear, disambiguate + mvncc r0, #0 @ (less than) r0<- -1 + moveq r0, #0 @ (equal) r0<- 0, trumps less than + bx r4 + @ Test for NaN with a second comparison. EABI forbids testing bit + @ patterns, and we can't represent 0x7fc00000 in immediate form, so + @ make the library call. +.LTEMPLATE_CMPG_FLOAT_gt_or_nan: + mov r1, r9 @ reverse order + mov r0, r10 + LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if < + movcc r0, #1 @ (greater than) r1<- 1 + bxcc r4 + mov r0, #1 @ r1<- 1 or -1 for NaN + bx r4 + + + + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_CMPL_FLOAT +dvmCompiler_TEMPLATE_CMPL_FLOAT: +/* File: armv5te/TEMPLATE_CMPL_FLOAT.S */ + /* + * For the JIT: incoming arguments in r0, r1 + * result in r0 + * + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into r1 depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1,1}; // one or both operands was NaN + * + * The straightforward implementation requires 3 calls to functions + * that return a result in r0. We can do it with two calls if our + * EABI library supports __aeabi_cfcmple (only one if we want to check + * for NaN directly): + * check x <= y + * if <, return -1 + * if ==, return 0 + * check y <= x + * if <, return 1 + * return {-1,1} + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + mov r4, lr @ save return address + mov r9, r0 @ Save copies - we may need to redo + mov r10, r1 + LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq + bhi .LTEMPLATE_CMPL_FLOAT_gt_or_nan @ C set and Z clear, disambiguate + mvncc r0, #0 @ (less than) r0<- -1 + moveq r0, #0 @ (equal) r0<- 0, trumps less than + bx r4 + @ Test for NaN with a second comparison. EABI forbids testing bit + @ patterns, and we can't represent 0x7fc00000 in immediate form, so + @ make the library call. +.LTEMPLATE_CMPL_FLOAT_gt_or_nan: + mov r1, r9 @ reverse order + mov r0, r10 + LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if < + movcc r0, #1 @ (greater than) r1<- 1 + bxcc r4 + mvn r0, #0 @ r1<- 1 or -1 for NaN + bx r4 + + + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_MUL_LONG +dvmCompiler_TEMPLATE_MUL_LONG: +/* File: armv5te/TEMPLATE_MUL_LONG.S */ + /* + * Signed 64-bit integer multiply. + * + * For JIT: op1 in r0/r1, op2 in r2/r3, return in r0/r1 + * + * Consider WXxYZ (r1r0 x r3r2) with a long multiply: + * WX + * x YZ + * -------- + * ZW ZX + * YW YX + * + * The low word of the result holds ZX, the high word holds + * (ZW+YX) + (the high overflow from ZX). YW doesn't matter because + * it doesn't fit in the low 64 bits. + * + * Unlike most ARM math operations, multiply instructions have + * restrictions on using the same register more than once (Rd and Rm + * cannot be the same). + */ + /* mul-long vAA, vBB, vCC */ + mul ip, r2, r1 @ ip<- ZxW + umull r9, r10, r2, r0 @ r9/r10 <- ZxX + mla r2, r0, r3, ip @ r2<- YxX + (ZxW) + add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX)) + mov r0,r9 + mov r1,r10 + bx lr + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_SHL_LONG +dvmCompiler_TEMPLATE_SHL_LONG: +/* File: armv5te/TEMPLATE_SHL_LONG.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low + * 6 bits. + */ + /* shl-long vAA, vBB, vCC */ + and r2, r2, #63 @ r2<- r2 & 0x3f + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r0, r0, asl r2 @ r0<- r0 << r2 + bx lr + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_SHR_LONG +dvmCompiler_TEMPLATE_SHR_LONG: +/* File: armv5te/TEMPLATE_SHR_LONG.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low + * 6 bits. + */ + /* shr-long vAA, vBB, vCC */ + and r2, r2, #63 @ r0<- r0 & 0x3f + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r1, r1, asr r2 @ r1<- r1 >> r2 + bx lr + + +/* ------------------------------ */ + .balign 4 + .global dvmCompiler_TEMPLATE_USHR_LONG +dvmCompiler_TEMPLATE_USHR_LONG: +/* File: armv5te/TEMPLATE_USHR_LONG.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to ignore all but the low + * 6 bits. + */ + /* ushr-long vAA, vBB, vCC */ + and r2, r2, #63 @ r0<- r0 & 0x3f + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + bx lr + + + .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart +/* File: armv5te/footer.S */ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 +.LinvokeNative: + @ Prep for the native call + @ r1 = newFP, r0 = methodToCall + ldr r3, [rGLUE, #offGlue_self] @ r3<- glue->self + ldr r9, [r3, #offThread_jniLocal_nextEntry] @ r9<- thread->refNext + str r1, [r3, #offThread_curFrame] @ self->curFrame = newFp + str r9, [r1, #(offStackSaveArea_localRefTop - sizeofStackSaveArea)] + @ newFp->localRefTop=refNext + mov r9, r3 @ r9<- glue->self (preserve) + SAVEAREA_FROM_FP(r10, r1) @ r10<- new stack save area + + mov r2, r0 @ r2<- methodToCall + mov r0, r1 @ r0<- newFP + add r1, rGLUE, #offGlue_retval @ r1<- &retval + + LDR_PC_LR "[r2, #offMethod_nativeFunc]" + + @ native return; r9=self, r10=newSaveArea + @ equivalent to dvmPopJniLocals + ldr r2, [r10, #offStackSaveArea_returnAddr] @ r2 = chaining cell ret + ldr r0, [r10, #offStackSaveArea_localRefTop] @ r0<- newSave->localRefTop + ldr r1, [r9, #offThread_exception] @ check for exception + str rFP, [r9, #offThread_curFrame] @ self->curFrame = fp + cmp r1, #0 @ null? + str r0, [r9, #offThread_jniLocal_nextEntry] @ self->refNext<- r0 + bne .LhandleException @ no, handle exception + bx r2 + +/* FIXME - untested */ +.LhandleException: + ldr rIBASE, .LdvmAsmInstructionStart + ldr rPC, [r10, #offStackSaveArea_savedPc] @ reload rPC + b dvmMterpCommonExceptionThrown + + .align 2 +.LdvmAsmInstructionStart: + .word dvmAsmInstructionStart +.LdvmJitToInterpNoChain: + .word dvmJitToInterpNoChain +.LdvmMterpStdBail: + .word dvmMterpStdBail +.L__aeabi_cdcmple: + .word __aeabi_cdcmple +.L__aeabi_cfcmple: + .word __aeabi_cfcmple + + .global dmvCompilerTemplateEnd +dmvCompilerTemplateEnd: + +#endif /* WITH_JIT */ + diff --git a/vm/compiler/template/rebuild.sh b/vm/compiler/template/rebuild.sh new file mode 100755 index 000000000..07d951650 --- /dev/null +++ b/vm/compiler/template/rebuild.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# +# Copyright (C) 2008 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. + +# +# Rebuild for all known targets. Necessary until the stuff in "out" gets +# generated as part of the build. +# +set -e +for arch in armv5te; do TARGET_ARCH_EXT=$arch make -f Makefile-template; done + |