blob: d1259b74848a9e924b7e98b2b95fcf4e649e3e0f [file] [log] [blame]
buzbee67bf8852011-08-17 17:51:35 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "Dalvik.h"
18#include "CompilerInternals.h"
19#include "Dataflow.h"
Ian Rogers0571d352011-11-03 19:51:38 -070020#include "leb128.h"
Brian Carlstrom9baa4ae2011-09-01 21:14:14 -070021#include "object.h"
Brian Carlstrom1f870082011-08-23 16:02:11 -070022#include "runtime.h"
buzbee67bf8852011-08-17 17:51:35 -070023
Elliott Hughes11d1b0c2012-01-23 16:57:47 -080024namespace art {
25
buzbee692be802012-08-29 15:52:59 -070026#if defined(ART_USE_QUICK_COMPILER)
TDYa12755e5e6c2012-09-11 15:14:42 -070027QuickCompiler::QuickCompiler() {
buzbee692be802012-08-29 15:52:59 -070028 // Create context, module, intrinsic helper & ir builder
29 llvm_context_.reset(new llvm::LLVMContext());
TDYa12755e5e6c2012-09-11 15:14:42 -070030 llvm_module_ = new llvm::Module("art", *llvm_context_);
buzbee692be802012-08-29 15:52:59 -070031 llvm::StructType::create(*llvm_context_, "JavaObject");
buzbee692be802012-08-29 15:52:59 -070032 intrinsic_helper_.reset( new greenland::IntrinsicHelper(*llvm_context_, *llvm_module_));
33 ir_builder_.reset(new greenland::IRBuilder(*llvm_context_, *llvm_module_, *intrinsic_helper_));
34}
35
36QuickCompiler::~QuickCompiler() {
37}
38
39extern "C" void ArtInitQuickCompilerContext(art::Compiler& compiler) {
40 CHECK(compiler.GetCompilerContext() == NULL);
TDYa12755e5e6c2012-09-11 15:14:42 -070041 QuickCompiler* quickCompiler = new QuickCompiler();
buzbee692be802012-08-29 15:52:59 -070042 compiler.SetCompilerContext(quickCompiler);
43}
44
45extern "C" void ArtUnInitQuickCompilerContext(art::Compiler& compiler) {
46 delete reinterpret_cast<QuickCompiler*>(compiler.GetCompilerContext());
47 compiler.SetCompilerContext(NULL);
48}
49#endif
50
buzbeece302932011-10-04 14:32:18 -070051/* Default optimizer/debug setting for the compiler. */
Elliott Hughese52e49b2012-04-02 16:05:44 -070052static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimizations
Bill Buzbeea114add2012-05-03 15:00:40 -070053 //(1 << kLoadStoreElimination) |
54 //(1 << kLoadHoisting) |
55 //(1 << kSuppressLoads) |
56 //(1 << kNullCheckElimination) |
57 //(1 << kPromoteRegs) |
58 //(1 << kTrackLiveTemps) |
59 //(1 << kSkipLargeMethodOptimization) |
60 //(1 << kSafeOptimizations) |
61 //(1 << kBBOpt) |
62 //(1 << kMatch) |
63 //(1 << kPromoteCompilerTemps) |
64 0;
buzbeece302932011-10-04 14:32:18 -070065
Elliott Hughese52e49b2012-04-02 16:05:44 -070066static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes
Bill Buzbeea114add2012-05-03 15:00:40 -070067 //(1 << kDebugDisplayMissingTargets) |
68 //(1 << kDebugVerbose) |
69 //(1 << kDebugDumpCFG) |
70 //(1 << kDebugSlowFieldPath) |
71 //(1 << kDebugSlowInvokePath) |
72 //(1 << kDebugSlowStringPath) |
73 //(1 << kDebugSlowestFieldPath) |
74 //(1 << kDebugSlowestStringPath) |
75 //(1 << kDebugExerciseResolveMethod) |
76 //(1 << kDebugVerifyDataflow) |
77 //(1 << kDebugShowMemoryUsage) |
78 //(1 << kDebugShowNops) |
79 //(1 << kDebugCountOpcodes) |
buzbeed1643e42012-09-05 14:06:51 -070080 //(1 << kDebugDumpCheckStats) |
buzbeead8f15e2012-06-18 14:49:45 -070081#if defined(ART_USE_QUICK_COMPILER)
82 //(1 << kDebugDumpBitcodeFile) |
Bill Buzbeec9f40dd2012-08-15 11:35:25 -070083 //(1 << kDebugVerifyBitcode) |
buzbeead8f15e2012-06-18 14:49:45 -070084#endif
Bill Buzbeea114add2012-05-03 15:00:40 -070085 0;
buzbeece302932011-10-04 14:32:18 -070086
buzbee31a4a6f2012-02-28 15:36:15 -080087inline bool contentIsInsn(const u2* codePtr) {
Bill Buzbeea114add2012-05-03 15:00:40 -070088 u2 instr = *codePtr;
89 Instruction::Code opcode = (Instruction::Code)(instr & 0xff);
buzbee67bf8852011-08-17 17:51:35 -070090
Bill Buzbeea114add2012-05-03 15:00:40 -070091 /*
92 * Since the low 8-bit in metadata may look like NOP, we need to check
93 * both the low and whole sub-word to determine whether it is code or data.
94 */
95 return (opcode != Instruction::NOP || instr == 0);
buzbee67bf8852011-08-17 17:51:35 -070096}
97
98/*
99 * Parse an instruction, return the length of the instruction
100 */
buzbee31a4a6f2012-02-28 15:36:15 -0800101inline int parseInsn(CompilationUnit* cUnit, const u2* codePtr,
Bill Buzbeea114add2012-05-03 15:00:40 -0700102 DecodedInstruction* decoded_instruction, bool printMe)
buzbee67bf8852011-08-17 17:51:35 -0700103{
Elliott Hughesadb8c672012-03-06 16:49:32 -0800104 // Don't parse instruction data
105 if (!contentIsInsn(codePtr)) {
106 return 0;
107 }
buzbee67bf8852011-08-17 17:51:35 -0700108
Elliott Hughesadb8c672012-03-06 16:49:32 -0800109 const Instruction* instruction = Instruction::At(codePtr);
110 *decoded_instruction = DecodedInstruction(instruction);
buzbee67bf8852011-08-17 17:51:35 -0700111
Elliott Hughesadb8c672012-03-06 16:49:32 -0800112 if (printMe) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700113 char* decodedString = oatGetDalvikDisassembly(cUnit, *decoded_instruction,
114 NULL);
115 LOG(INFO) << codePtr << ": 0x"
116 << std::hex << static_cast<int>(decoded_instruction->opcode)
Elliott Hughesadb8c672012-03-06 16:49:32 -0800117 << " " << decodedString;
118 }
119 return instruction->SizeInCodeUnits();
buzbee67bf8852011-08-17 17:51:35 -0700120}
121
122#define UNKNOWN_TARGET 0xffffffff
123
Elliott Hughesadb8c672012-03-06 16:49:32 -0800124inline bool isGoto(MIR* insn) {
125 switch (insn->dalvikInsn.opcode) {
126 case Instruction::GOTO:
127 case Instruction::GOTO_16:
128 case Instruction::GOTO_32:
129 return true;
130 default:
131 return false;
Bill Buzbeea114add2012-05-03 15:00:40 -0700132 }
buzbee67bf8852011-08-17 17:51:35 -0700133}
134
135/*
136 * Identify unconditional branch instructions
137 */
Elliott Hughesadb8c672012-03-06 16:49:32 -0800138inline bool isUnconditionalBranch(MIR* insn) {
139 switch (insn->dalvikInsn.opcode) {
140 case Instruction::RETURN_VOID:
141 case Instruction::RETURN:
142 case Instruction::RETURN_WIDE:
143 case Instruction::RETURN_OBJECT:
144 return true;
Bill Buzbeea114add2012-05-03 15:00:40 -0700145 default:
146 return isGoto(insn);
Elliott Hughesadb8c672012-03-06 16:49:32 -0800147 }
buzbee67bf8852011-08-17 17:51:35 -0700148}
149
150/* Split an existing block from the specified code offset into two */
buzbee31a4a6f2012-02-28 15:36:15 -0800151BasicBlock *splitBlock(CompilationUnit* cUnit, unsigned int codeOffset,
Bill Buzbeea114add2012-05-03 15:00:40 -0700152 BasicBlock* origBlock, BasicBlock** immedPredBlockP)
buzbee67bf8852011-08-17 17:51:35 -0700153{
Bill Buzbeea114add2012-05-03 15:00:40 -0700154 MIR* insn = origBlock->firstMIRInsn;
155 while (insn) {
156 if (insn->offset == codeOffset) break;
157 insn = insn->next;
158 }
159 if (insn == NULL) {
160 LOG(FATAL) << "Break split failed";
161 }
162 BasicBlock *bottomBlock = oatNewBB(cUnit, kDalvikByteCode,
163 cUnit->numBlocks++);
164 oatInsertGrowableList(cUnit, &cUnit->blockList, (intptr_t) bottomBlock);
buzbee67bf8852011-08-17 17:51:35 -0700165
Bill Buzbeea114add2012-05-03 15:00:40 -0700166 bottomBlock->startOffset = codeOffset;
167 bottomBlock->firstMIRInsn = insn;
168 bottomBlock->lastMIRInsn = origBlock->lastMIRInsn;
buzbee67bf8852011-08-17 17:51:35 -0700169
Bill Buzbeea114add2012-05-03 15:00:40 -0700170 /* Add it to the quick lookup cache */
171 cUnit->blockMap.Put(bottomBlock->startOffset, bottomBlock);
buzbee5b537102012-01-17 17:33:47 -0800172
Bill Buzbeea114add2012-05-03 15:00:40 -0700173 /* Handle the taken path */
174 bottomBlock->taken = origBlock->taken;
175 if (bottomBlock->taken) {
176 origBlock->taken = NULL;
177 oatDeleteGrowableList(bottomBlock->taken->predecessors,
buzbeeba938cb2012-02-03 14:47:55 -0800178 (intptr_t)origBlock);
Bill Buzbeea114add2012-05-03 15:00:40 -0700179 oatInsertGrowableList(cUnit, bottomBlock->taken->predecessors,
180 (intptr_t)bottomBlock);
181 }
182
183 /* Handle the fallthrough path */
Bill Buzbeea114add2012-05-03 15:00:40 -0700184 bottomBlock->fallThrough = origBlock->fallThrough;
185 origBlock->fallThrough = bottomBlock;
Bill Buzbeea114add2012-05-03 15:00:40 -0700186 oatInsertGrowableList(cUnit, bottomBlock->predecessors,
187 (intptr_t)origBlock);
188 if (bottomBlock->fallThrough) {
189 oatDeleteGrowableList(bottomBlock->fallThrough->predecessors,
190 (intptr_t)origBlock);
191 oatInsertGrowableList(cUnit, bottomBlock->fallThrough->predecessors,
192 (intptr_t)bottomBlock);
193 }
194
195 /* Handle the successor list */
196 if (origBlock->successorBlockList.blockListType != kNotUsed) {
197 bottomBlock->successorBlockList = origBlock->successorBlockList;
198 origBlock->successorBlockList.blockListType = kNotUsed;
199 GrowableListIterator iterator;
200
201 oatGrowableListIteratorInit(&bottomBlock->successorBlockList.blocks,
202 &iterator);
203 while (true) {
204 SuccessorBlockInfo *successorBlockInfo =
205 (SuccessorBlockInfo *) oatGrowableListIteratorNext(&iterator);
206 if (successorBlockInfo == NULL) break;
207 BasicBlock *bb = successorBlockInfo->block;
208 oatDeleteGrowableList(bb->predecessors, (intptr_t)origBlock);
209 oatInsertGrowableList(cUnit, bb->predecessors, (intptr_t)bottomBlock);
buzbee67bf8852011-08-17 17:51:35 -0700210 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700211 }
buzbee67bf8852011-08-17 17:51:35 -0700212
Bill Buzbeea114add2012-05-03 15:00:40 -0700213 origBlock->lastMIRInsn = insn->prev;
buzbee67bf8852011-08-17 17:51:35 -0700214
Bill Buzbeea114add2012-05-03 15:00:40 -0700215 insn->prev->next = NULL;
216 insn->prev = NULL;
217 /*
218 * Update the immediate predecessor block pointer so that outgoing edges
219 * can be applied to the proper block.
220 */
221 if (immedPredBlockP) {
222 DCHECK_EQ(*immedPredBlockP, origBlock);
223 *immedPredBlockP = bottomBlock;
224 }
225 return bottomBlock;
buzbee67bf8852011-08-17 17:51:35 -0700226}
227
228/*
229 * Given a code offset, find out the block that starts with it. If the offset
buzbee9ab05de2012-01-18 15:43:48 -0800230 * is in the middle of an existing block, split it into two. If immedPredBlockP
231 * is not non-null and is the block being split, update *immedPredBlockP to
232 * point to the bottom block so that outgoing edges can be set up properly
233 * (by the caller)
buzbee5b537102012-01-17 17:33:47 -0800234 * Utilizes a map for fast lookup of the typical cases.
buzbee67bf8852011-08-17 17:51:35 -0700235 */
buzbee31a4a6f2012-02-28 15:36:15 -0800236BasicBlock *findBlock(CompilationUnit* cUnit, unsigned int codeOffset,
237 bool split, bool create, BasicBlock** immedPredBlockP)
buzbee67bf8852011-08-17 17:51:35 -0700238{
Bill Buzbeea114add2012-05-03 15:00:40 -0700239 GrowableList* blockList = &cUnit->blockList;
240 BasicBlock* bb;
241 unsigned int i;
242 SafeMap<unsigned int, BasicBlock*>::iterator it;
buzbee67bf8852011-08-17 17:51:35 -0700243
Bill Buzbeea114add2012-05-03 15:00:40 -0700244 it = cUnit->blockMap.find(codeOffset);
245 if (it != cUnit->blockMap.end()) {
246 return it->second;
247 } else if (!create) {
248 return NULL;
249 }
250
251 if (split) {
252 for (i = 0; i < blockList->numUsed; i++) {
253 bb = (BasicBlock *) blockList->elemList[i];
254 if (bb->blockType != kDalvikByteCode) continue;
255 /* Check if a branch jumps into the middle of an existing block */
256 if ((codeOffset > bb->startOffset) && (bb->lastMIRInsn != NULL) &&
257 (codeOffset <= bb->lastMIRInsn->offset)) {
258 BasicBlock *newBB = splitBlock(cUnit, codeOffset, bb,
259 bb == *immedPredBlockP ?
260 immedPredBlockP : NULL);
261 return newBB;
262 }
buzbee5b537102012-01-17 17:33:47 -0800263 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700264 }
buzbee5b537102012-01-17 17:33:47 -0800265
Bill Buzbeea114add2012-05-03 15:00:40 -0700266 /* Create a new one */
267 bb = oatNewBB(cUnit, kDalvikByteCode, cUnit->numBlocks++);
268 oatInsertGrowableList(cUnit, &cUnit->blockList, (intptr_t) bb);
269 bb->startOffset = codeOffset;
270 cUnit->blockMap.Put(bb->startOffset, bb);
271 return bb;
buzbee67bf8852011-08-17 17:51:35 -0700272}
273
buzbeef58c12c2012-07-03 15:06:29 -0700274/* Find existing block */
275BasicBlock* oatFindBlock(CompilationUnit* cUnit, unsigned int codeOffset)
276{
277 return findBlock(cUnit, codeOffset, false, false, NULL);
278}
279
buzbeead8f15e2012-06-18 14:49:45 -0700280/* Turn method name into a legal Linux file name */
281void oatReplaceSpecialChars(std::string& str)
282{
283 static const struct { const char before; const char after; } match[] =
284 {{'/','-'}, {';','#'}, {' ','#'}, {'$','+'},
285 {'(','@'}, {')','@'}, {'<','='}, {'>','='}};
286 for (unsigned int i = 0; i < sizeof(match)/sizeof(match[0]); i++) {
287 std::replace(str.begin(), str.end(), match[i].before, match[i].after);
288 }
289}
290
buzbee67bf8852011-08-17 17:51:35 -0700291/* Dump the CFG into a DOT graph */
292void oatDumpCFG(CompilationUnit* cUnit, const char* dirPrefix)
293{
Bill Buzbeea114add2012-05-03 15:00:40 -0700294 FILE* file;
buzbeead8f15e2012-06-18 14:49:45 -0700295 std::string fname(PrettyMethod(cUnit->method_idx, *cUnit->dex_file));
296 oatReplaceSpecialChars(fname);
297 fname = StringPrintf("%s%s%x.dot", dirPrefix, fname.c_str(),
298 cUnit->entryBlock->fallThrough->startOffset);
299 file = fopen(fname.c_str(), "w");
Bill Buzbeea114add2012-05-03 15:00:40 -0700300 if (file == NULL) {
301 return;
302 }
303 fprintf(file, "digraph G {\n");
304
305 fprintf(file, " rankdir=TB\n");
306
307 int numReachableBlocks = cUnit->numReachableBlocks;
308 int idx;
309 const GrowableList *blockList = &cUnit->blockList;
310
311 for (idx = 0; idx < numReachableBlocks; idx++) {
312 int blockIdx = cUnit->dfsOrder.elemList[idx];
313 BasicBlock *bb = (BasicBlock *) oatGrowableListGetElement(blockList,
314 blockIdx);
315 if (bb == NULL) break;
buzbeed1643e42012-09-05 14:06:51 -0700316 if (bb->blockType == kDead) continue;
Bill Buzbeea114add2012-05-03 15:00:40 -0700317 if (bb->blockType == kEntryBlock) {
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700318 fprintf(file, " entry_%d [shape=Mdiamond];\n", bb->id);
Bill Buzbeea114add2012-05-03 15:00:40 -0700319 } else if (bb->blockType == kExitBlock) {
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700320 fprintf(file, " exit_%d [shape=Mdiamond];\n", bb->id);
Bill Buzbeea114add2012-05-03 15:00:40 -0700321 } else if (bb->blockType == kDalvikByteCode) {
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700322 fprintf(file, " block%04x_%d [shape=record,label = \"{ \\\n",
323 bb->startOffset, bb->id);
Bill Buzbeea114add2012-05-03 15:00:40 -0700324 const MIR *mir;
325 fprintf(file, " {block id %d\\l}%s\\\n", bb->id,
326 bb->firstMIRInsn ? " | " : " ");
327 for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
328 fprintf(file, " {%04x %s\\l}%s\\\n", mir->offset,
329 mir->ssaRep ? oatFullDisassembler(cUnit, mir) :
330 Instruction::Name(mir->dalvikInsn.opcode),
331 mir->next ? " | " : " ");
332 }
333 fprintf(file, " }\"];\n\n");
334 } else if (bb->blockType == kExceptionHandling) {
335 char blockName[BLOCK_NAME_LEN];
336
337 oatGetBlockName(bb, blockName);
338 fprintf(file, " %s [shape=invhouse];\n", blockName);
buzbee67bf8852011-08-17 17:51:35 -0700339 }
buzbee67bf8852011-08-17 17:51:35 -0700340
Bill Buzbeea114add2012-05-03 15:00:40 -0700341 char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
buzbee67bf8852011-08-17 17:51:35 -0700342
Bill Buzbeea114add2012-05-03 15:00:40 -0700343 if (bb->taken) {
344 oatGetBlockName(bb, blockName1);
345 oatGetBlockName(bb->taken, blockName2);
346 fprintf(file, " %s:s -> %s:n [style=dotted]\n",
347 blockName1, blockName2);
buzbee67bf8852011-08-17 17:51:35 -0700348 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700349 if (bb->fallThrough) {
350 oatGetBlockName(bb, blockName1);
351 oatGetBlockName(bb->fallThrough, blockName2);
352 fprintf(file, " %s:s -> %s:n\n", blockName1, blockName2);
353 }
354
355 if (bb->successorBlockList.blockListType != kNotUsed) {
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700356 fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n",
357 bb->startOffset, bb->id,
Bill Buzbeea114add2012-05-03 15:00:40 -0700358 (bb->successorBlockList.blockListType == kCatch) ?
359 "Mrecord" : "record");
360 GrowableListIterator iterator;
361 oatGrowableListIteratorInit(&bb->successorBlockList.blocks,
362 &iterator);
363 SuccessorBlockInfo *successorBlockInfo =
364 (SuccessorBlockInfo *) oatGrowableListIteratorNext(&iterator);
365
366 int succId = 0;
367 while (true) {
368 if (successorBlockInfo == NULL) break;
369
370 BasicBlock *destBlock = successorBlockInfo->block;
371 SuccessorBlockInfo *nextSuccessorBlockInfo =
372 (SuccessorBlockInfo *) oatGrowableListIteratorNext(&iterator);
373
374 fprintf(file, " {<f%d> %04x: %04x\\l}%s\\\n",
375 succId++,
376 successorBlockInfo->key,
377 destBlock->startOffset,
378 (nextSuccessorBlockInfo != NULL) ? " | " : " ");
379
380 successorBlockInfo = nextSuccessorBlockInfo;
381 }
382 fprintf(file, " }\"];\n\n");
383
384 oatGetBlockName(bb, blockName1);
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700385 fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n",
386 blockName1, bb->startOffset, bb->id);
Bill Buzbeea114add2012-05-03 15:00:40 -0700387
388 if (bb->successorBlockList.blockListType == kPackedSwitch ||
389 bb->successorBlockList.blockListType == kSparseSwitch) {
390
391 oatGrowableListIteratorInit(&bb->successorBlockList.blocks,
392 &iterator);
393
394 succId = 0;
395 while (true) {
396 SuccessorBlockInfo *successorBlockInfo = (SuccessorBlockInfo *)
397 oatGrowableListIteratorNext(&iterator);
398 if (successorBlockInfo == NULL) break;
399
400 BasicBlock *destBlock = successorBlockInfo->block;
401
402 oatGetBlockName(destBlock, blockName2);
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700403 fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->startOffset,
404 bb->id, succId++, blockName2);
Bill Buzbeea114add2012-05-03 15:00:40 -0700405 }
406 }
407 }
408 fprintf(file, "\n");
409
410 /* Display the dominator tree */
411 oatGetBlockName(bb, blockName1);
412 fprintf(file, " cfg%s [label=\"%s\", shape=none];\n",
413 blockName1, blockName1);
414 if (bb->iDom) {
415 oatGetBlockName(bb->iDom, blockName2);
416 fprintf(file, " cfg%s:s -> cfg%s:n\n\n", blockName2, blockName1);
417 }
418 }
419 fprintf(file, "}\n");
420 fclose(file);
buzbee67bf8852011-08-17 17:51:35 -0700421}
422
423/* Verify if all the successor is connected with all the claimed predecessors */
buzbee31a4a6f2012-02-28 15:36:15 -0800424bool verifyPredInfo(CompilationUnit* cUnit, BasicBlock* bb)
buzbee67bf8852011-08-17 17:51:35 -0700425{
Bill Buzbeea114add2012-05-03 15:00:40 -0700426 GrowableListIterator iter;
buzbee67bf8852011-08-17 17:51:35 -0700427
Bill Buzbeea114add2012-05-03 15:00:40 -0700428 oatGrowableListIteratorInit(bb->predecessors, &iter);
429 while (true) {
430 BasicBlock *predBB = (BasicBlock*)oatGrowableListIteratorNext(&iter);
431 if (!predBB) break;
432 bool found = false;
433 if (predBB->taken == bb) {
434 found = true;
435 } else if (predBB->fallThrough == bb) {
436 found = true;
437 } else if (predBB->successorBlockList.blockListType != kNotUsed) {
438 GrowableListIterator iterator;
439 oatGrowableListIteratorInit(&predBB->successorBlockList.blocks,
440 &iterator);
441 while (true) {
442 SuccessorBlockInfo *successorBlockInfo = (SuccessorBlockInfo *)
443 oatGrowableListIteratorNext(&iterator);
444 if (successorBlockInfo == NULL) break;
445 BasicBlock *succBB = successorBlockInfo->block;
446 if (succBB == bb) {
buzbee67bf8852011-08-17 17:51:35 -0700447 found = true;
Bill Buzbeea114add2012-05-03 15:00:40 -0700448 break;
buzbee67bf8852011-08-17 17:51:35 -0700449 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700450 }
buzbee67bf8852011-08-17 17:51:35 -0700451 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700452 if (found == false) {
453 char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
454 oatGetBlockName(bb, blockName1);
455 oatGetBlockName(predBB, blockName2);
456 oatDumpCFG(cUnit, "/sdcard/cfg/");
457 LOG(FATAL) << "Successor " << blockName1 << "not found from "
458 << blockName2;
459 }
460 }
461 return true;
buzbee67bf8852011-08-17 17:51:35 -0700462}
463
464/* Identify code range in try blocks and set up the empty catch blocks */
buzbee31a4a6f2012-02-28 15:36:15 -0800465void processTryCatchBlocks(CompilationUnit* cUnit)
buzbee67bf8852011-08-17 17:51:35 -0700466{
Bill Buzbeea114add2012-05-03 15:00:40 -0700467 const DexFile::CodeItem* code_item = cUnit->code_item;
468 int triesSize = code_item->tries_size_;
469 int offset;
buzbee67bf8852011-08-17 17:51:35 -0700470
Bill Buzbeea114add2012-05-03 15:00:40 -0700471 if (triesSize == 0) {
472 return;
473 }
474
475 ArenaBitVector* tryBlockAddr = cUnit->tryBlockAddr;
476
477 for (int i = 0; i < triesSize; i++) {
478 const DexFile::TryItem* pTry =
479 DexFile::GetTryItems(*code_item, i);
480 int startOffset = pTry->start_addr_;
481 int endOffset = startOffset + pTry->insn_count_;
482 for (offset = startOffset; offset < endOffset; offset++) {
483 oatSetBit(cUnit, tryBlockAddr, offset);
buzbee67bf8852011-08-17 17:51:35 -0700484 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700485 }
buzbee67bf8852011-08-17 17:51:35 -0700486
Bill Buzbeea114add2012-05-03 15:00:40 -0700487 // Iterate over each of the handlers to enqueue the empty Catch blocks
488 const byte* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0);
489 uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
490 for (uint32_t idx = 0; idx < handlers_size; idx++) {
491 CatchHandlerIterator iterator(handlers_ptr);
492 for (; iterator.HasNext(); iterator.Next()) {
493 uint32_t address = iterator.GetHandlerAddress();
494 findBlock(cUnit, address, false /* split */, true /*create*/,
495 /* immedPredBlockP */ NULL);
buzbee67bf8852011-08-17 17:51:35 -0700496 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700497 handlers_ptr = iterator.EndDataPointer();
498 }
buzbee67bf8852011-08-17 17:51:35 -0700499}
500
Elliott Hughesadb8c672012-03-06 16:49:32 -0800501/* Process instructions with the kBranch flag */
buzbee31a4a6f2012-02-28 15:36:15 -0800502BasicBlock* processCanBranch(CompilationUnit* cUnit, BasicBlock* curBlock,
Bill Buzbeea114add2012-05-03 15:00:40 -0700503 MIR* insn, int curOffset, int width, int flags,
504 const u2* codePtr, const u2* codeEnd)
buzbee67bf8852011-08-17 17:51:35 -0700505{
Bill Buzbeea114add2012-05-03 15:00:40 -0700506 int target = curOffset;
507 switch (insn->dalvikInsn.opcode) {
508 case Instruction::GOTO:
509 case Instruction::GOTO_16:
510 case Instruction::GOTO_32:
511 target += (int) insn->dalvikInsn.vA;
512 break;
513 case Instruction::IF_EQ:
514 case Instruction::IF_NE:
515 case Instruction::IF_LT:
516 case Instruction::IF_GE:
517 case Instruction::IF_GT:
518 case Instruction::IF_LE:
buzbee0967a252012-09-14 10:43:54 -0700519 curBlock->conditionalBranch = true;
Bill Buzbeea114add2012-05-03 15:00:40 -0700520 target += (int) insn->dalvikInsn.vC;
521 break;
522 case Instruction::IF_EQZ:
523 case Instruction::IF_NEZ:
524 case Instruction::IF_LTZ:
525 case Instruction::IF_GEZ:
526 case Instruction::IF_GTZ:
527 case Instruction::IF_LEZ:
buzbee0967a252012-09-14 10:43:54 -0700528 curBlock->conditionalBranch = true;
Bill Buzbeea114add2012-05-03 15:00:40 -0700529 target += (int) insn->dalvikInsn.vB;
530 break;
531 default:
532 LOG(FATAL) << "Unexpected opcode(" << (int)insn->dalvikInsn.opcode
533 << ") with kBranch set";
534 }
535 BasicBlock *takenBlock = findBlock(cUnit, target,
536 /* split */
537 true,
538 /* create */
539 true,
540 /* immedPredBlockP */
541 &curBlock);
542 curBlock->taken = takenBlock;
543 oatInsertGrowableList(cUnit, takenBlock->predecessors, (intptr_t)curBlock);
buzbee67bf8852011-08-17 17:51:35 -0700544
Bill Buzbeea114add2012-05-03 15:00:40 -0700545 /* Always terminate the current block for conditional branches */
546 if (flags & Instruction::kContinue) {
547 BasicBlock *fallthroughBlock = findBlock(cUnit,
548 curOffset + width,
549 /*
550 * If the method is processed
551 * in sequential order from the
552 * beginning, we don't need to
553 * specify split for continue
554 * blocks. However, this
555 * routine can be called by
556 * compileLoop, which starts
557 * parsing the method from an
558 * arbitrary address in the
559 * method body.
560 */
561 true,
562 /* create */
563 true,
564 /* immedPredBlockP */
565 &curBlock);
566 curBlock->fallThrough = fallthroughBlock;
567 oatInsertGrowableList(cUnit, fallthroughBlock->predecessors,
568 (intptr_t)curBlock);
569 } else if (codePtr < codeEnd) {
570 /* Create a fallthrough block for real instructions (incl. NOP) */
571 if (contentIsInsn(codePtr)) {
572 findBlock(cUnit, curOffset + width,
573 /* split */
574 false,
575 /* create */
576 true,
577 /* immedPredBlockP */
578 NULL);
buzbee67bf8852011-08-17 17:51:35 -0700579 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700580 }
581 return curBlock;
buzbee67bf8852011-08-17 17:51:35 -0700582}
583
Elliott Hughesadb8c672012-03-06 16:49:32 -0800584/* Process instructions with the kSwitch flag */
buzbee31a4a6f2012-02-28 15:36:15 -0800585void processCanSwitch(CompilationUnit* cUnit, BasicBlock* curBlock,
586 MIR* insn, int curOffset, int width, int flags)
buzbee67bf8852011-08-17 17:51:35 -0700587{
Bill Buzbeea114add2012-05-03 15:00:40 -0700588 u2* switchData= (u2 *) (cUnit->insns + curOffset + insn->dalvikInsn.vB);
589 int size;
590 int* keyTable;
591 int* targetTable;
592 int i;
593 int firstKey;
buzbee67bf8852011-08-17 17:51:35 -0700594
Bill Buzbeea114add2012-05-03 15:00:40 -0700595 /*
596 * Packed switch data format:
597 * ushort ident = 0x0100 magic value
598 * ushort size number of entries in the table
599 * int first_key first (and lowest) switch case value
600 * int targets[size] branch targets, relative to switch opcode
601 *
602 * Total size is (4+size*2) 16-bit code units.
603 */
604 if (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) {
605 DCHECK_EQ(static_cast<int>(switchData[0]),
606 static_cast<int>(Instruction::kPackedSwitchSignature));
607 size = switchData[1];
608 firstKey = switchData[2] | (switchData[3] << 16);
609 targetTable = (int *) &switchData[4];
610 keyTable = NULL; // Make the compiler happy
611 /*
612 * Sparse switch data format:
613 * ushort ident = 0x0200 magic value
614 * ushort size number of entries in the table; > 0
615 * int keys[size] keys, sorted low-to-high; 32-bit aligned
616 * int targets[size] branch targets, relative to switch opcode
617 *
618 * Total size is (2+size*4) 16-bit code units.
619 */
620 } else {
621 DCHECK_EQ(static_cast<int>(switchData[0]),
622 static_cast<int>(Instruction::kSparseSwitchSignature));
623 size = switchData[1];
624 keyTable = (int *) &switchData[2];
625 targetTable = (int *) &switchData[2 + size*2];
626 firstKey = 0; // To make the compiler happy
627 }
buzbee67bf8852011-08-17 17:51:35 -0700628
Bill Buzbeea114add2012-05-03 15:00:40 -0700629 if (curBlock->successorBlockList.blockListType != kNotUsed) {
630 LOG(FATAL) << "Successor block list already in use: "
631 << (int)curBlock->successorBlockList.blockListType;
632 }
633 curBlock->successorBlockList.blockListType =
634 (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
635 kPackedSwitch : kSparseSwitch;
636 oatInitGrowableList(cUnit, &curBlock->successorBlockList.blocks, size,
637 kListSuccessorBlocks);
638
639 for (i = 0; i < size; i++) {
640 BasicBlock *caseBlock = findBlock(cUnit, curOffset + targetTable[i],
641 /* split */
642 true,
643 /* create */
644 true,
645 /* immedPredBlockP */
646 &curBlock);
647 SuccessorBlockInfo *successorBlockInfo =
648 (SuccessorBlockInfo *) oatNew(cUnit, sizeof(SuccessorBlockInfo),
649 false, kAllocSuccessor);
650 successorBlockInfo->block = caseBlock;
651 successorBlockInfo->key =
Elliott Hughesadb8c672012-03-06 16:49:32 -0800652 (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
Bill Buzbeea114add2012-05-03 15:00:40 -0700653 firstKey + i : keyTable[i];
654 oatInsertGrowableList(cUnit, &curBlock->successorBlockList.blocks,
655 (intptr_t) successorBlockInfo);
656 oatInsertGrowableList(cUnit, caseBlock->predecessors,
buzbeeba938cb2012-02-03 14:47:55 -0800657 (intptr_t)curBlock);
Bill Buzbeea114add2012-05-03 15:00:40 -0700658 }
659
660 /* Fall-through case */
661 BasicBlock* fallthroughBlock = findBlock(cUnit,
662 curOffset + width,
663 /* split */
664 false,
665 /* create */
666 true,
667 /* immedPredBlockP */
668 NULL);
669 curBlock->fallThrough = fallthroughBlock;
670 oatInsertGrowableList(cUnit, fallthroughBlock->predecessors,
671 (intptr_t)curBlock);
buzbee67bf8852011-08-17 17:51:35 -0700672}
673
Elliott Hughesadb8c672012-03-06 16:49:32 -0800674/* Process instructions with the kThrow flag */
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700675BasicBlock* processCanThrow(CompilationUnit* cUnit, BasicBlock* curBlock,
676 MIR* insn, int curOffset, int width, int flags,
677 ArenaBitVector* tryBlockAddr, const u2* codePtr,
678 const u2* codeEnd)
buzbee67bf8852011-08-17 17:51:35 -0700679{
Bill Buzbeea114add2012-05-03 15:00:40 -0700680 const DexFile::CodeItem* code_item = cUnit->code_item;
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700681 bool inTryBlock = oatIsBitSet(tryBlockAddr, curOffset);
buzbee67bf8852011-08-17 17:51:35 -0700682
Bill Buzbeea114add2012-05-03 15:00:40 -0700683 /* In try block */
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700684 if (inTryBlock) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700685 CatchHandlerIterator iterator(*code_item, curOffset);
buzbee67bf8852011-08-17 17:51:35 -0700686
Bill Buzbeea114add2012-05-03 15:00:40 -0700687 if (curBlock->successorBlockList.blockListType != kNotUsed) {
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700688 LOG(INFO) << PrettyMethod(cUnit->method_idx, *cUnit->dex_file);
Bill Buzbeea114add2012-05-03 15:00:40 -0700689 LOG(FATAL) << "Successor block list already in use: "
690 << (int)curBlock->successorBlockList.blockListType;
buzbee67bf8852011-08-17 17:51:35 -0700691 }
692
Bill Buzbeea114add2012-05-03 15:00:40 -0700693 curBlock->successorBlockList.blockListType = kCatch;
694 oatInitGrowableList(cUnit, &curBlock->successorBlockList.blocks, 2,
695 kListSuccessorBlocks);
696
697 for (;iterator.HasNext(); iterator.Next()) {
698 BasicBlock *catchBlock = findBlock(cUnit, iterator.GetHandlerAddress(),
699 false /* split*/,
700 false /* creat */,
701 NULL /* immedPredBlockP */);
702 catchBlock->catchEntry = true;
buzbee6459e7c2012-10-02 14:42:41 -0700703 cUnit->catches.insert(catchBlock->startOffset);
Bill Buzbeea114add2012-05-03 15:00:40 -0700704 SuccessorBlockInfo *successorBlockInfo = (SuccessorBlockInfo *)
705 oatNew(cUnit, sizeof(SuccessorBlockInfo), false, kAllocSuccessor);
706 successorBlockInfo->block = catchBlock;
707 successorBlockInfo->key = iterator.GetHandlerTypeIndex();
708 oatInsertGrowableList(cUnit, &curBlock->successorBlockList.blocks,
709 (intptr_t) successorBlockInfo);
710 oatInsertGrowableList(cUnit, catchBlock->predecessors,
711 (intptr_t)curBlock);
buzbee67bf8852011-08-17 17:51:35 -0700712 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700713 } else {
714 BasicBlock *ehBlock = oatNewBB(cUnit, kExceptionHandling,
715 cUnit->numBlocks++);
716 curBlock->taken = ehBlock;
717 oatInsertGrowableList(cUnit, &cUnit->blockList, (intptr_t) ehBlock);
718 ehBlock->startOffset = curOffset;
719 oatInsertGrowableList(cUnit, ehBlock->predecessors, (intptr_t)curBlock);
720 }
721
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700722 if (insn->dalvikInsn.opcode == Instruction::THROW){
buzbee0967a252012-09-14 10:43:54 -0700723 curBlock->explicitThrow = true;
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700724 if ((codePtr < codeEnd) && contentIsInsn(codePtr)) {
725 // Force creation of new block following THROW via side-effect
726 findBlock(cUnit, curOffset + width, /* split */ false,
727 /* create */ true, /* immedPredBlockP */ NULL);
728 }
729 if (!inTryBlock) {
730 // Don't split a THROW that can't rethrow - we're done.
731 return curBlock;
Bill Buzbeea114add2012-05-03 15:00:40 -0700732 }
733 }
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700734
735 /*
736 * Split the potentially-throwing instruction into two parts.
737 * The first half will be a pseudo-op that captures the exception
738 * edges and terminates the basic block. It always falls through.
739 * Then, create a new basic block that begins with the throwing instruction
740 * (minus exceptions). Note: this new basic block must NOT be entered into
741 * the blockMap. If the potentially-throwing instruction is the target of a
742 * future branch, we need to find the check psuedo half. The new
743 * basic block containing the work portion of the instruction should
744 * only be entered via fallthrough from the block containing the
745 * pseudo exception edge MIR. Note also that this new block is
746 * not automatically terminated after the work portion, and may
747 * contain following instructions.
748 */
749 BasicBlock *newBlock = oatNewBB(cUnit, kDalvikByteCode, cUnit->numBlocks++);
750 oatInsertGrowableList(cUnit, &cUnit->blockList, (intptr_t)newBlock);
751 newBlock->startOffset = insn->offset;
752 curBlock->fallThrough = newBlock;
753 oatInsertGrowableList(cUnit, newBlock->predecessors, (intptr_t)curBlock);
754 MIR* newInsn = (MIR*)oatNew(cUnit, sizeof(MIR), true, kAllocMIR);
755 *newInsn = *insn;
756 insn->dalvikInsn.opcode =
757 static_cast<Instruction::Code>(kMirOpCheck);
758 // Associate the two halves
759 insn->meta.throwInsn = newInsn;
760 newInsn->meta.throwInsn = insn;
761 oatAppendMIR(newBlock, newInsn);
762 return newBlock;
buzbee67bf8852011-08-17 17:51:35 -0700763}
764
Elliott Hughesb3bd5f02012-03-08 21:05:27 -0800765void oatInit(CompilationUnit* cUnit, const Compiler& compiler) {
766 if (!oatArchInit()) {
767 LOG(FATAL) << "Failed to initialize oat";
768 }
769 if (!oatHeapInit(cUnit)) {
770 LOG(FATAL) << "Failed to initialize oat heap";
771 }
772}
773
buzbeeabc4c6b2012-08-23 08:17:15 -0700774CompiledMethod* compileMethod(Compiler& compiler,
775 const DexFile::CodeItem* code_item,
776 uint32_t access_flags, InvokeType invoke_type,
777 uint32_t method_idx, jobject class_loader,
778 const DexFile& dex_file
779#if defined(ART_USE_QUICK_COMPILER)
TDYa12755e5e6c2012-09-11 15:14:42 -0700780 , QuickCompiler* quick_compiler,
buzbeeabc4c6b2012-08-23 08:17:15 -0700781 bool gbcOnly
782#endif
783 )
buzbee67bf8852011-08-17 17:51:35 -0700784{
Bill Buzbeea114add2012-05-03 15:00:40 -0700785 VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
Brian Carlstrom94496d32011-08-22 09:22:47 -0700786
Bill Buzbeea114add2012-05-03 15:00:40 -0700787 const u2* codePtr = code_item->insns_;
788 const u2* codeEnd = code_item->insns_ + code_item->insns_size_in_code_units_;
789 int numBlocks = 0;
790 unsigned int curOffset = 0;
buzbee67bf8852011-08-17 17:51:35 -0700791
Bill Buzbeea114add2012-05-03 15:00:40 -0700792 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
793 UniquePtr<CompilationUnit> cUnit(new CompilationUnit);
buzbeeba938cb2012-02-03 14:47:55 -0800794
Bill Buzbeea114add2012-05-03 15:00:40 -0700795 oatInit(cUnit.get(), compiler);
Elliott Hughesb3bd5f02012-03-08 21:05:27 -0800796
Bill Buzbeea114add2012-05-03 15:00:40 -0700797 cUnit->compiler = &compiler;
798 cUnit->class_linker = class_linker;
799 cUnit->dex_file = &dex_file;
Bill Buzbeea114add2012-05-03 15:00:40 -0700800 cUnit->method_idx = method_idx;
801 cUnit->code_item = code_item;
802 cUnit->access_flags = access_flags;
Ian Rogers08f753d2012-08-24 14:35:25 -0700803 cUnit->invoke_type = invoke_type;
Bill Buzbeea114add2012-05-03 15:00:40 -0700804 cUnit->shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx));
805 cUnit->instructionSet = compiler.GetInstructionSet();
806 cUnit->insns = code_item->insns_;
807 cUnit->insnsSize = code_item->insns_size_in_code_units_;
808 cUnit->numIns = code_item->ins_size_;
809 cUnit->numRegs = code_item->registers_size_ - cUnit->numIns;
810 cUnit->numOuts = code_item->outs_size_;
buzbee2cfc6392012-05-07 14:51:40 -0700811#if defined(ART_USE_QUICK_COMPILER)
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700812 DCHECK((cUnit->instructionSet == kThumb2) ||
813 (cUnit->instructionSet == kX86) ||
814 (cUnit->instructionSet == kMips));
TDYa12755e5e6c2012-09-11 15:14:42 -0700815 if (gbcOnly) {
816 cUnit->quick_compiler = quick_compiler;
817 } else {
818 // TODO: We need one LLVMContext per thread.
819 cUnit->quick_compiler =
820 reinterpret_cast<QuickCompiler*>(compiler.GetCompilerContext());
821 }
822 DCHECK(cUnit->quick_compiler != NULL);
Bill Buzbeec9f40dd2012-08-15 11:35:25 -0700823 if (cUnit->instructionSet == kThumb2) {
824 // TODO: remove this once x86 is tested
buzbee85eee022012-07-16 22:12:38 -0700825 cUnit->genBitcode = true;
826 }
buzbee2a83e8f2012-07-13 16:42:30 -0700827#endif
Bill Buzbeea114add2012-05-03 15:00:40 -0700828 /* Adjust this value accordingly once inlining is performed */
829 cUnit->numDalvikRegisters = code_item->registers_size_;
830 // TODO: set this from command line
831 cUnit->compilerFlipMatch = false;
832 bool useMatch = !cUnit->compilerMethodMatch.empty();
833 bool match = useMatch && (cUnit->compilerFlipMatch ^
834 (PrettyMethod(method_idx, dex_file).find(cUnit->compilerMethodMatch) !=
835 std::string::npos));
836 if (!useMatch || match) {
837 cUnit->disableOpt = kCompilerOptimizerDisableFlags;
838 cUnit->enableDebug = kCompilerDebugFlags;
839 cUnit->printMe = VLOG_IS_ON(compiler) ||
840 (cUnit->enableDebug & (1 << kDebugVerbose));
841 }
buzbee2cfc6392012-05-07 14:51:40 -0700842#if defined(ART_USE_QUICK_COMPILER)
buzbee6969d502012-06-15 16:40:31 -0700843 if (cUnit->genBitcode) {
buzbee6459e7c2012-10-02 14:42:41 -0700844#ifndef NDEBUG
845 cUnit->enableDebug |= (1 << kDebugVerifyBitcode);
846#endif
buzbee2a83e8f2012-07-13 16:42:30 -0700847 //cUnit->printMe = true;
848 //cUnit->enableDebug |= (1 << kDebugDumpBitcodeFile);
buzbee6969d502012-06-15 16:40:31 -0700849 }
buzbee2cfc6392012-05-07 14:51:40 -0700850#endif
jeffhao7fbee072012-08-24 17:56:54 -0700851 if (cUnit->instructionSet == kMips) {
852 // Disable some optimizations for mips for now
853 cUnit->disableOpt |= (
854 (1 << kLoadStoreElimination) |
855 (1 << kLoadHoisting) |
856 (1 << kSuppressLoads) |
857 (1 << kNullCheckElimination) |
858 (1 << kPromoteRegs) |
859 (1 << kTrackLiveTemps) |
860 (1 << kSkipLargeMethodOptimization) |
861 (1 << kSafeOptimizations) |
862 (1 << kBBOpt) |
863 (1 << kMatch) |
864 (1 << kPromoteCompilerTemps));
865 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700866 /* Are we generating code for the debugger? */
867 if (compiler.IsDebuggingSupported()) {
868 cUnit->genDebugger = true;
869 // Yes, disable most optimizations
870 cUnit->disableOpt |= (
871 (1 << kLoadStoreElimination) |
872 (1 << kLoadHoisting) |
873 (1 << kSuppressLoads) |
874 (1 << kPromoteRegs) |
875 (1 << kBBOpt) |
876 (1 << kMatch) |
877 (1 << kTrackLiveTemps));
878 }
879
880 /* Gathering opcode stats? */
881 if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
882 cUnit->opcodeCount = (int*)oatNew(cUnit.get(),
883 kNumPackedOpcodes * sizeof(int), true, kAllocMisc);
884 }
885
886 /* Assume non-throwing leaf */
887 cUnit->attrs = (METHOD_IS_LEAF | METHOD_IS_THROW_FREE);
888
889 /* Initialize the block list, estimate size based on insnsSize */
890 oatInitGrowableList(cUnit.get(), &cUnit->blockList, cUnit->insnsSize,
891 kListBlockList);
892
893 /* Initialize the switchTables list */
894 oatInitGrowableList(cUnit.get(), &cUnit->switchTables, 4,
895 kListSwitchTables);
896
897 /* Intialize the fillArrayData list */
898 oatInitGrowableList(cUnit.get(), &cUnit->fillArrayData, 4,
899 kListFillArrayData);
900
901 /* Intialize the throwLaunchpads list, estimate size based on insnsSize */
902 oatInitGrowableList(cUnit.get(), &cUnit->throwLaunchpads, cUnit->insnsSize,
903 kListThrowLaunchPads);
904
905 /* Intialize the instrinsicLaunchpads list */
906 oatInitGrowableList(cUnit.get(), &cUnit->intrinsicLaunchpads, 4,
907 kListMisc);
908
909
910 /* Intialize the suspendLaunchpads list */
911 oatInitGrowableList(cUnit.get(), &cUnit->suspendLaunchpads, 2048,
912 kListSuspendLaunchPads);
913
914 /* Allocate the bit-vector to track the beginning of basic blocks */
915 ArenaBitVector *tryBlockAddr = oatAllocBitVector(cUnit.get(),
916 cUnit->insnsSize,
917 true /* expandable */);
918 cUnit->tryBlockAddr = tryBlockAddr;
919
920 /* Create the default entry and exit blocks and enter them to the list */
921 BasicBlock *entryBlock = oatNewBB(cUnit.get(), kEntryBlock, numBlocks++);
922 BasicBlock *exitBlock = oatNewBB(cUnit.get(), kExitBlock, numBlocks++);
923
924 cUnit->entryBlock = entryBlock;
925 cUnit->exitBlock = exitBlock;
926
927 oatInsertGrowableList(cUnit.get(), &cUnit->blockList, (intptr_t) entryBlock);
928 oatInsertGrowableList(cUnit.get(), &cUnit->blockList, (intptr_t) exitBlock);
929
930 /* Current block to record parsed instructions */
931 BasicBlock *curBlock = oatNewBB(cUnit.get(), kDalvikByteCode, numBlocks++);
932 curBlock->startOffset = 0;
933 oatInsertGrowableList(cUnit.get(), &cUnit->blockList, (intptr_t) curBlock);
934 /* Add first block to the fast lookup cache */
935 cUnit->blockMap.Put(curBlock->startOffset, curBlock);
936 entryBlock->fallThrough = curBlock;
937 oatInsertGrowableList(cUnit.get(), curBlock->predecessors,
938 (intptr_t)entryBlock);
939
940 /*
941 * Store back the number of blocks since new blocks may be created of
942 * accessing cUnit.
943 */
944 cUnit->numBlocks = numBlocks;
945
946 /* Identify code range in try blocks and set up the empty catch blocks */
947 processTryCatchBlocks(cUnit.get());
948
949 /* Set up for simple method detection */
950 int numPatterns = sizeof(specialPatterns)/sizeof(specialPatterns[0]);
951 bool livePattern = (numPatterns > 0) && !(cUnit->disableOpt & (1 << kMatch));
Elliott Hughesabe64aa2012-05-30 17:34:45 -0700952 bool* deadPattern = (bool*)oatNew(cUnit.get(), sizeof(bool) * numPatterns, true,
Bill Buzbeea114add2012-05-03 15:00:40 -0700953 kAllocMisc);
954 SpecialCaseHandler specialCase = kNoHandler;
955 int patternPos = 0;
956
957 /* Parse all instructions and put them into containing basic blocks */
958 while (codePtr < codeEnd) {
959 MIR *insn = (MIR *) oatNew(cUnit.get(), sizeof(MIR), true, kAllocMIR);
960 insn->offset = curOffset;
961 int width = parseInsn(cUnit.get(), codePtr, &insn->dalvikInsn, false);
962 insn->width = width;
963 Instruction::Code opcode = insn->dalvikInsn.opcode;
964 if (cUnit->opcodeCount != NULL) {
965 cUnit->opcodeCount[static_cast<int>(opcode)]++;
buzbee44b412b2012-02-04 08:50:53 -0800966 }
967
Bill Buzbeea114add2012-05-03 15:00:40 -0700968 /* Terminate when the data section is seen */
969 if (width == 0)
970 break;
971
972 /* Possible simple method? */
973 if (livePattern) {
974 livePattern = false;
975 specialCase = kNoHandler;
976 for (int i = 0; i < numPatterns; i++) {
977 if (!deadPattern[i]) {
978 if (specialPatterns[i].opcodes[patternPos] == opcode) {
979 livePattern = true;
980 specialCase = specialPatterns[i].handlerCode;
981 } else {
982 deadPattern[i] = true;
983 }
984 }
985 }
986 patternPos++;
buzbeea7c12682012-03-19 13:13:53 -0700987 }
988
Bill Buzbeea114add2012-05-03 15:00:40 -0700989 oatAppendMIR(curBlock, insn);
buzbeecefd1872011-09-09 09:59:52 -0700990
Bill Buzbeea114add2012-05-03 15:00:40 -0700991 codePtr += width;
Ian Rogersa75a0132012-09-28 11:41:42 -0700992 int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode);
buzbee67bf8852011-08-17 17:51:35 -0700993
Bill Buzbeea114add2012-05-03 15:00:40 -0700994 int dfFlags = oatDataFlowAttributes[insn->dalvikInsn.opcode];
buzbee67bf8852011-08-17 17:51:35 -0700995
Bill Buzbeea114add2012-05-03 15:00:40 -0700996 if (dfFlags & DF_HAS_DEFS) {
buzbeebff24652012-05-06 16:22:05 -0700997 cUnit->defCount += (dfFlags & DF_A_WIDE) ? 2 : 1;
Bill Buzbeea114add2012-05-03 15:00:40 -0700998 }
buzbee67bf8852011-08-17 17:51:35 -0700999
Bill Buzbeea114add2012-05-03 15:00:40 -07001000 if (flags & Instruction::kBranch) {
1001 curBlock = processCanBranch(cUnit.get(), curBlock, insn, curOffset,
1002 width, flags, codePtr, codeEnd);
1003 } else if (flags & Instruction::kReturn) {
1004 curBlock->fallThrough = exitBlock;
1005 oatInsertGrowableList(cUnit.get(), exitBlock->predecessors,
1006 (intptr_t)curBlock);
1007 /*
1008 * Terminate the current block if there are instructions
1009 * afterwards.
1010 */
1011 if (codePtr < codeEnd) {
1012 /*
1013 * Create a fallthrough block for real instructions
1014 * (incl. NOP).
1015 */
1016 if (contentIsInsn(codePtr)) {
1017 findBlock(cUnit.get(), curOffset + width,
1018 /* split */
1019 false,
1020 /* create */
1021 true,
1022 /* immedPredBlockP */
1023 NULL);
1024 }
1025 }
1026 } else if (flags & Instruction::kThrow) {
Bill Buzbeec9f40dd2012-08-15 11:35:25 -07001027 curBlock = processCanThrow(cUnit.get(), curBlock, insn, curOffset,
1028 width, flags, tryBlockAddr, codePtr, codeEnd);
Bill Buzbeea114add2012-05-03 15:00:40 -07001029 } else if (flags & Instruction::kSwitch) {
1030 processCanSwitch(cUnit.get(), curBlock, insn, curOffset, width, flags);
1031 }
1032 curOffset += width;
1033 BasicBlock *nextBlock = findBlock(cUnit.get(), curOffset,
1034 /* split */
1035 false,
1036 /* create */
1037 false,
1038 /* immedPredBlockP */
1039 NULL);
1040 if (nextBlock) {
1041 /*
1042 * The next instruction could be the target of a previously parsed
1043 * forward branch so a block is already created. If the current
1044 * instruction is not an unconditional branch, connect them through
1045 * the fall-through link.
1046 */
1047 DCHECK(curBlock->fallThrough == NULL ||
1048 curBlock->fallThrough == nextBlock ||
1049 curBlock->fallThrough == exitBlock);
buzbee5ade1d22011-09-09 14:44:52 -07001050
Bill Buzbeea114add2012-05-03 15:00:40 -07001051 if ((curBlock->fallThrough == NULL) && (flags & Instruction::kContinue)) {
1052 curBlock->fallThrough = nextBlock;
1053 oatInsertGrowableList(cUnit.get(), nextBlock->predecessors,
1054 (intptr_t)curBlock);
1055 }
1056 curBlock = nextBlock;
1057 }
1058 }
buzbeefc9e6fa2012-03-23 15:14:29 -07001059
Bill Buzbeea114add2012-05-03 15:00:40 -07001060 if (!(cUnit->disableOpt & (1 << kSkipLargeMethodOptimization))) {
1061 if ((cUnit->numBlocks > MANY_BLOCKS) ||
1062 ((cUnit->numBlocks > MANY_BLOCKS_INITIALIZER) &&
1063 PrettyMethod(method_idx, dex_file, false).find("init>") !=
1064 std::string::npos)) {
1065 cUnit->qdMode = true;
1066 }
1067 }
buzbeefc9e6fa2012-03-23 15:14:29 -07001068
Bill Buzbeea114add2012-05-03 15:00:40 -07001069 if (cUnit->qdMode) {
buzbeed1643e42012-09-05 14:06:51 -07001070#if !defined(ART_USE_QUICK_COMPILER)
1071 // Bitcode generation requires full dataflow analysis
Bill Buzbeea114add2012-05-03 15:00:40 -07001072 cUnit->disableDataflow = true;
buzbeed1643e42012-09-05 14:06:51 -07001073#endif
Bill Buzbeea114add2012-05-03 15:00:40 -07001074 // Disable optimization which require dataflow/ssa
1075 cUnit->disableOpt |=
buzbeed1643e42012-09-05 14:06:51 -07001076#if !defined(ART_USE_QUICK_COMPILER)
Bill Buzbeea114add2012-05-03 15:00:40 -07001077 (1 << kNullCheckElimination) |
buzbeed1643e42012-09-05 14:06:51 -07001078#endif
Bill Buzbeea114add2012-05-03 15:00:40 -07001079 (1 << kBBOpt) |
1080 (1 << kPromoteRegs);
1081 if (cUnit->printMe) {
1082 LOG(INFO) << "QD mode enabled: "
1083 << PrettyMethod(method_idx, dex_file)
1084 << " too big: " << cUnit->numBlocks;
1085 }
1086 }
buzbeec1f45042011-09-21 16:03:19 -07001087
Bill Buzbeea114add2012-05-03 15:00:40 -07001088 if (cUnit->printMe) {
1089 oatDumpCompilationUnit(cUnit.get());
1090 }
buzbee67bf8852011-08-17 17:51:35 -07001091
buzbee0967a252012-09-14 10:43:54 -07001092 /* Do a code layout pass */
1093 oatMethodCodeLayout(cUnit.get());
1094
Bill Buzbeea114add2012-05-03 15:00:40 -07001095 if (cUnit->enableDebug & (1 << kDebugVerifyDataflow)) {
1096 /* Verify if all blocks are connected as claimed */
1097 oatDataFlowAnalysisDispatcher(cUnit.get(), verifyPredInfo, kAllNodes,
1098 false /* isIterative */);
1099 }
buzbee67bf8852011-08-17 17:51:35 -07001100
Bill Buzbeea114add2012-05-03 15:00:40 -07001101 /* Perform SSA transformation for the whole method */
1102 oatMethodSSATransformation(cUnit.get());
buzbee67bf8852011-08-17 17:51:35 -07001103
buzbee2cfc6392012-05-07 14:51:40 -07001104 /* Do constant propagation */
1105 // TODO: Probably need to make these expandable to support new ssa names
1106 // introducted during MIR optimization passes
1107 cUnit->isConstantV = oatAllocBitVector(cUnit.get(), cUnit->numSSARegs,
1108 false /* not expandable */);
1109 cUnit->constantValues =
1110 (int*)oatNew(cUnit.get(), sizeof(int) * cUnit->numSSARegs, true,
1111 kAllocDFInfo);
1112 oatDataFlowAnalysisDispatcher(cUnit.get(), oatDoConstantPropagation,
1113 kAllNodes,
1114 false /* isIterative */);
1115
Bill Buzbeea114add2012-05-03 15:00:40 -07001116 /* Detect loops */
1117 oatMethodLoopDetection(cUnit.get());
buzbee67bf8852011-08-17 17:51:35 -07001118
Bill Buzbeea114add2012-05-03 15:00:40 -07001119 /* Count uses */
1120 oatMethodUseCount(cUnit.get());
buzbee67bf8852011-08-17 17:51:35 -07001121
Bill Buzbeea114add2012-05-03 15:00:40 -07001122 /* Perform null check elimination */
1123 oatMethodNullCheckElimination(cUnit.get());
1124
buzbeed1643e42012-09-05 14:06:51 -07001125 /* Combine basic blocks where possible */
1126 oatMethodBasicBlockCombine(cUnit.get());
1127
Bill Buzbeea114add2012-05-03 15:00:40 -07001128 /* Do some basic block optimizations */
1129 oatMethodBasicBlockOptimization(cUnit.get());
1130
buzbeed1643e42012-09-05 14:06:51 -07001131 if (cUnit->enableDebug & (1 << kDebugDumpCheckStats)) {
1132 oatDumpCheckStats(cUnit.get());
1133 }
1134
Bill Buzbeea114add2012-05-03 15:00:40 -07001135 oatInitializeRegAlloc(cUnit.get()); // Needs to happen after SSA naming
1136
1137 /* Allocate Registers using simple local allocation scheme */
1138 oatSimpleRegAlloc(cUnit.get());
1139
buzbee2cfc6392012-05-07 14:51:40 -07001140#if defined(ART_USE_QUICK_COMPILER)
1141 /* Go the LLVM path? */
1142 if (cUnit->genBitcode) {
1143 // MIR->Bitcode
1144 oatMethodMIR2Bitcode(cUnit.get());
TDYa12755e5e6c2012-09-11 15:14:42 -07001145 if (gbcOnly) {
buzbeeabc4c6b2012-08-23 08:17:15 -07001146 // all done
1147 oatArenaReset(cUnit.get());
1148 return NULL;
1149 }
buzbee2cfc6392012-05-07 14:51:40 -07001150 // Bitcode->LIR
1151 oatMethodBitcode2LIR(cUnit.get());
1152 } else {
1153#endif
1154 if (specialCase != kNoHandler) {
1155 /*
1156 * Custom codegen for special cases. If for any reason the
1157 * special codegen doesn't succeed, cUnit->firstLIRInsn will
1158 * set to NULL;
1159 */
1160 oatSpecialMIR2LIR(cUnit.get(), specialCase);
1161 }
buzbee67bf8852011-08-17 17:51:35 -07001162
buzbee2cfc6392012-05-07 14:51:40 -07001163 /* Convert MIR to LIR, etc. */
1164 if (cUnit->firstLIRInsn == NULL) {
1165 oatMethodMIR2LIR(cUnit.get());
1166 }
1167#if defined(ART_USE_QUICK_COMPILER)
Bill Buzbeea114add2012-05-03 15:00:40 -07001168 }
buzbee2cfc6392012-05-07 14:51:40 -07001169#endif
buzbee67bf8852011-08-17 17:51:35 -07001170
Bill Buzbeea114add2012-05-03 15:00:40 -07001171 // Debugging only
1172 if (cUnit->enableDebug & (1 << kDebugDumpCFG)) {
1173 oatDumpCFG(cUnit.get(), "/sdcard/cfg/");
1174 }
buzbee16da88c2012-03-20 10:38:17 -07001175
Bill Buzbeea114add2012-05-03 15:00:40 -07001176 /* Method is not empty */
1177 if (cUnit->firstLIRInsn) {
buzbee67bf8852011-08-17 17:51:35 -07001178
Bill Buzbeea114add2012-05-03 15:00:40 -07001179 // mark the targets of switch statement case labels
1180 oatProcessSwitchTables(cUnit.get());
buzbee67bf8852011-08-17 17:51:35 -07001181
Bill Buzbeea114add2012-05-03 15:00:40 -07001182 /* Convert LIR into machine code. */
1183 oatAssembleLIR(cUnit.get());
buzbee99ba9642012-01-25 14:23:14 -08001184
Elliott Hughes3b6baaa2011-10-14 19:13:56 -07001185 if (cUnit->printMe) {
Bill Buzbeea114add2012-05-03 15:00:40 -07001186 oatCodegenDump(cUnit.get());
buzbee67bf8852011-08-17 17:51:35 -07001187 }
1188
Bill Buzbeea114add2012-05-03 15:00:40 -07001189 if (cUnit->opcodeCount != NULL) {
1190 LOG(INFO) << "Opcode Count";
1191 for (int i = 0; i < kNumPackedOpcodes; i++) {
1192 if (cUnit->opcodeCount[i] != 0) {
1193 LOG(INFO) << "-C- "
1194 << Instruction::Name(static_cast<Instruction::Code>(i))
1195 << " " << cUnit->opcodeCount[i];
buzbee67bf8852011-08-17 17:51:35 -07001196 }
Bill Buzbeea114add2012-05-03 15:00:40 -07001197 }
1198 }
1199 }
buzbeea7c12682012-03-19 13:13:53 -07001200
Bill Buzbeea114add2012-05-03 15:00:40 -07001201 // Combine vmap tables - core regs, then fp regs - into vmapTable
1202 std::vector<uint16_t> vmapTable;
buzbeeca7a5e42012-08-20 11:12:18 -07001203 // Core regs may have been inserted out of order - sort first
1204 std::sort(cUnit->coreVmapTable.begin(), cUnit->coreVmapTable.end());
Bill Buzbeea114add2012-05-03 15:00:40 -07001205 for (size_t i = 0 ; i < cUnit->coreVmapTable.size(); i++) {
buzbeeca7a5e42012-08-20 11:12:18 -07001206 // Copy, stripping out the phys register sort key
1207 vmapTable.push_back(~(-1 << VREG_NUM_WIDTH) & cUnit->coreVmapTable[i]);
Bill Buzbeea114add2012-05-03 15:00:40 -07001208 }
1209 // If we have a frame, push a marker to take place of lr
1210 if (cUnit->frameSize > 0) {
1211 vmapTable.push_back(INVALID_VREG);
1212 } else {
1213 DCHECK_EQ(__builtin_popcount(cUnit->coreSpillMask), 0);
1214 DCHECK_EQ(__builtin_popcount(cUnit->fpSpillMask), 0);
1215 }
buzbeeca7a5e42012-08-20 11:12:18 -07001216 // Combine vmap tables - core regs, then fp regs. fp regs already sorted
Bill Buzbeea114add2012-05-03 15:00:40 -07001217 for (uint32_t i = 0; i < cUnit->fpVmapTable.size(); i++) {
1218 vmapTable.push_back(cUnit->fpVmapTable[i]);
1219 }
1220 CompiledMethod* result =
1221 new CompiledMethod(cUnit->instructionSet, cUnit->codeBuffer,
Bill Buzbeea5b30242012-09-28 07:19:44 -07001222 cUnit->frameSize, cUnit->coreSpillMask, cUnit->fpSpillMask,
1223 cUnit->combinedMappingTable, vmapTable, cUnit->nativeGcMap);
buzbee67bf8852011-08-17 17:51:35 -07001224
Bill Buzbeea114add2012-05-03 15:00:40 -07001225 VLOG(compiler) << "Compiled " << PrettyMethod(method_idx, dex_file)
1226 << " (" << (cUnit->codeBuffer.size() * sizeof(cUnit->codeBuffer[0]))
1227 << " bytes)";
buzbee5abfa3e2012-01-31 17:01:43 -08001228
1229#ifdef WITH_MEMSTATS
Bill Buzbeea114add2012-05-03 15:00:40 -07001230 if (cUnit->enableDebug & (1 << kDebugShowMemoryUsage)) {
1231 oatDumpMemStats(cUnit.get());
1232 }
buzbee5abfa3e2012-01-31 17:01:43 -08001233#endif
buzbee67bf8852011-08-17 17:51:35 -07001234
Bill Buzbeea114add2012-05-03 15:00:40 -07001235 oatArenaReset(cUnit.get());
buzbeeba938cb2012-02-03 14:47:55 -08001236
Bill Buzbeea114add2012-05-03 15:00:40 -07001237 return result;
buzbee67bf8852011-08-17 17:51:35 -07001238}
1239
buzbeeabc4c6b2012-08-23 08:17:15 -07001240#if defined(ART_USE_QUICK_COMPILER)
1241CompiledMethod* oatCompileMethod(Compiler& compiler,
1242 const DexFile::CodeItem* code_item,
1243 uint32_t access_flags, InvokeType invoke_type,
1244 uint32_t method_idx, jobject class_loader,
1245 const DexFile& dex_file)
1246{
1247 return compileMethod(compiler, code_item, access_flags, invoke_type, method_idx, class_loader,
TDYa12755e5e6c2012-09-11 15:14:42 -07001248 dex_file, NULL, false);
buzbeeabc4c6b2012-08-23 08:17:15 -07001249}
1250
1251/*
1252 * Given existing llvm module, context, intrinsic_helper and IRBuilder,
1253 * add the bitcode for the method described by code_item to the module.
1254 */
1255void oatCompileMethodToGBC(Compiler& compiler,
1256 const DexFile::CodeItem* code_item,
1257 uint32_t access_flags, InvokeType invoke_type,
1258 uint32_t method_idx, jobject class_loader,
1259 const DexFile& dex_file,
TDYa12755e5e6c2012-09-11 15:14:42 -07001260 QuickCompiler* quick_compiler)
buzbeeabc4c6b2012-08-23 08:17:15 -07001261{
1262 compileMethod(compiler, code_item, access_flags, invoke_type, method_idx, class_loader,
TDYa12755e5e6c2012-09-11 15:14:42 -07001263 dex_file, quick_compiler, true);
buzbeeabc4c6b2012-08-23 08:17:15 -07001264}
1265#else
1266CompiledMethod* oatCompileMethod(Compiler& compiler,
1267 const DexFile::CodeItem* code_item,
1268 uint32_t access_flags, InvokeType invoke_type,
1269 uint32_t method_idx, jobject class_loader,
1270 const DexFile& dex_file)
1271{
1272 return compileMethod(compiler, code_item, access_flags, invoke_type, method_idx, class_loader,
1273 dex_file);
1274}
1275#endif
1276
Elliott Hughes11d1b0c2012-01-23 16:57:47 -08001277} // namespace art
Elliott Hughesb3bd5f02012-03-08 21:05:27 -08001278
Shih-wei Liao46bde5c2012-08-31 11:28:16 -07001279#if !defined(ART_USE_LLVM_COMPILER)
Bill Buzbeea114add2012-05-03 15:00:40 -07001280extern "C" art::CompiledMethod*
1281 ArtCompileMethod(art::Compiler& compiler,
1282 const art::DexFile::CodeItem* code_item,
Ian Rogers08f753d2012-08-24 14:35:25 -07001283 uint32_t access_flags, art::InvokeType invoke_type,
1284 uint32_t method_idx, jobject class_loader,
Bill Buzbeea114add2012-05-03 15:00:40 -07001285 const art::DexFile& dex_file)
Elliott Hughesb3bd5f02012-03-08 21:05:27 -08001286{
1287 CHECK_EQ(compiler.GetInstructionSet(), art::oatInstructionSet());
Ian Rogers08f753d2012-08-24 14:35:25 -07001288 return art::oatCompileMethod(compiler, code_item, access_flags, invoke_type,
1289 method_idx, class_loader, dex_file);
Elliott Hughesb3bd5f02012-03-08 21:05:27 -08001290}
Shih-wei Liao46bde5c2012-08-31 11:28:16 -07001291#endif