blob: 766cdce23556437f04488b3cc6c39d8b864ed514 [file] [log] [blame]
buzbeee3acd072012-02-25 17:03:10 -08001/*
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
buzbee395116c2013-02-27 14:30:25 -080017#include "compiler/dex/compiler_internals.h"
Ian Rogers4f6ad8a2013-03-18 15:27:28 -070018#include "dex_file-inl.h"
Ian Rogers0c7abda2012-09-19 13:33:42 -070019#include "gc_map.h"
Ian Rogers8d3a1172013-06-04 01:13:28 -070020#include "mir_to_lir-inl.h"
Ian Rogers0c7abda2012-09-19 13:33:42 -070021#include "verifier/dex_gc_map.h"
22#include "verifier/method_verifier.h"
23
buzbeee3acd072012-02-25 17:03:10 -080024namespace art {
25
buzbee1fd33462013-03-25 13:40:45 -070026bool Mir2Lir::IsInexpensiveConstant(RegLocation rl_src)
buzbee4ef3e452012-12-14 13:35:28 -080027{
28 bool res = false;
29 if (rl_src.is_const) {
30 if (rl_src.wide) {
31 if (rl_src.fp) {
buzbee1fd33462013-03-25 13:40:45 -070032 res = InexpensiveConstantDouble(mir_graph_->ConstantValueWide(rl_src));
buzbee4ef3e452012-12-14 13:35:28 -080033 } else {
buzbee1fd33462013-03-25 13:40:45 -070034 res = InexpensiveConstantLong(mir_graph_->ConstantValueWide(rl_src));
buzbee4ef3e452012-12-14 13:35:28 -080035 }
36 } else {
37 if (rl_src.fp) {
buzbee1fd33462013-03-25 13:40:45 -070038 res = InexpensiveConstantFloat(mir_graph_->ConstantValue(rl_src));
buzbee4ef3e452012-12-14 13:35:28 -080039 } else {
buzbee1fd33462013-03-25 13:40:45 -070040 res = InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src));
buzbee4ef3e452012-12-14 13:35:28 -080041 }
42 }
43 }
44 return res;
45}
46
buzbee1fd33462013-03-25 13:40:45 -070047void Mir2Lir::MarkSafepointPC(LIR* inst)
buzbee02031b12012-11-23 09:41:35 -080048{
49 inst->def_mask = ENCODE_ALL;
buzbee1fd33462013-03-25 13:40:45 -070050 LIR* safepoint_pc = NewLIR0(kPseudoSafepointPC);
buzbee02031b12012-11-23 09:41:35 -080051 DCHECK_EQ(safepoint_pc->def_mask, ENCODE_ALL);
52}
53
buzbee1fd33462013-03-25 13:40:45 -070054bool Mir2Lir::FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put)
buzbee02031b12012-11-23 09:41:35 -080055{
buzbee1fd33462013-03-25 13:40:45 -070056 return cu_->compiler_driver->ComputeInstanceFieldInfo(
57 field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, is_volatile, is_put);
buzbee02031b12012-11-23 09:41:35 -080058}
59
buzbeecbd6d442012-11-17 14:11:25 -080060/* Convert an instruction to a NOP */
buzbee1fd33462013-03-25 13:40:45 -070061void Mir2Lir::NopLIR( LIR* lir)
buzbeecbd6d442012-11-17 14:11:25 -080062{
buzbeefa57c472012-11-21 12:06:18 -080063 lir->flags.is_nop = true;
buzbeecbd6d442012-11-17 14:11:25 -080064}
65
buzbee1fd33462013-03-25 13:40:45 -070066void Mir2Lir::SetMemRefType(LIR* lir, bool is_load, int mem_type)
buzbee31a4a6f2012-02-28 15:36:15 -080067{
buzbeefa57c472012-11-21 12:06:18 -080068 uint64_t *mask_ptr;
buzbeeeaf09bc2012-11-15 14:51:41 -080069 uint64_t mask = ENCODE_MEM;;
buzbee1fd33462013-03-25 13:40:45 -070070 DCHECK(GetTargetInstFlags(lir->opcode) & (IS_LOAD | IS_STORE));
buzbeefa57c472012-11-21 12:06:18 -080071 if (is_load) {
72 mask_ptr = &lir->use_mask;
Bill Buzbeea114add2012-05-03 15:00:40 -070073 } else {
buzbeefa57c472012-11-21 12:06:18 -080074 mask_ptr = &lir->def_mask;
Bill Buzbeea114add2012-05-03 15:00:40 -070075 }
76 /* Clear out the memref flags */
buzbeefa57c472012-11-21 12:06:18 -080077 *mask_ptr &= ~mask;
Bill Buzbeea114add2012-05-03 15:00:40 -070078 /* ..and then add back the one we need */
buzbeefa57c472012-11-21 12:06:18 -080079 switch (mem_type) {
Bill Buzbeea114add2012-05-03 15:00:40 -070080 case kLiteral:
buzbeefa57c472012-11-21 12:06:18 -080081 DCHECK(is_load);
82 *mask_ptr |= ENCODE_LITERAL;
Bill Buzbeea114add2012-05-03 15:00:40 -070083 break;
84 case kDalvikReg:
buzbeefa57c472012-11-21 12:06:18 -080085 *mask_ptr |= ENCODE_DALVIK_REG;
Bill Buzbeea114add2012-05-03 15:00:40 -070086 break;
87 case kHeapRef:
buzbeefa57c472012-11-21 12:06:18 -080088 *mask_ptr |= ENCODE_HEAP_REF;
Bill Buzbeea114add2012-05-03 15:00:40 -070089 break;
90 case kMustNotAlias:
91 /* Currently only loads can be marked as kMustNotAlias */
buzbee1fd33462013-03-25 13:40:45 -070092 DCHECK(!(GetTargetInstFlags(lir->opcode) & IS_STORE));
buzbeefa57c472012-11-21 12:06:18 -080093 *mask_ptr |= ENCODE_MUST_NOT_ALIAS;
Bill Buzbeea114add2012-05-03 15:00:40 -070094 break;
95 default:
buzbeefa57c472012-11-21 12:06:18 -080096 LOG(FATAL) << "Oat: invalid memref kind - " << mem_type;
Bill Buzbeea114add2012-05-03 15:00:40 -070097 }
buzbee31a4a6f2012-02-28 15:36:15 -080098}
99
100/*
Ian Rogersb5d09b22012-03-06 22:14:17 -0800101 * Mark load/store instructions that access Dalvik registers through the stack.
buzbee31a4a6f2012-02-28 15:36:15 -0800102 */
buzbee1fd33462013-03-25 13:40:45 -0700103void Mir2Lir::AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load,
104 bool is64bit)
buzbee31a4a6f2012-02-28 15:36:15 -0800105{
buzbee1fd33462013-03-25 13:40:45 -0700106 SetMemRefType(lir, is_load, kDalvikReg);
buzbee31a4a6f2012-02-28 15:36:15 -0800107
Bill Buzbeea114add2012-05-03 15:00:40 -0700108 /*
buzbeefa57c472012-11-21 12:06:18 -0800109 * Store the Dalvik register id in alias_info. Mark the MSB if it is a 64-bit
Bill Buzbeea114add2012-05-03 15:00:40 -0700110 * access.
111 */
buzbeefa57c472012-11-21 12:06:18 -0800112 lir->alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit);
buzbee31a4a6f2012-02-28 15:36:15 -0800113}
114
115/*
buzbee5de34942012-03-01 14:51:57 -0800116 * Debugging macros
117 */
118#define DUMP_RESOURCE_MASK(X)
buzbee5de34942012-03-01 14:51:57 -0800119
120/* Pretty-print a LIR instruction */
buzbee1fd33462013-03-25 13:40:45 -0700121void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr)
buzbee5de34942012-03-01 14:51:57 -0800122{
Bill Buzbeea114add2012-05-03 15:00:40 -0700123 int offset = lir->offset;
124 int dest = lir->operands[0];
buzbee1fd33462013-03-25 13:40:45 -0700125 const bool dump_nop = (cu_->enable_debug & (1 << kDebugShowNops));
buzbee5de34942012-03-01 14:51:57 -0800126
Bill Buzbeea114add2012-05-03 15:00:40 -0700127 /* Handle pseudo-ops individually, and all regular insns as a group */
128 switch (lir->opcode) {
129 case kPseudoMethodEntry:
130 LOG(INFO) << "-------- method entry "
buzbee1fd33462013-03-25 13:40:45 -0700131 << PrettyMethod(cu_->method_idx, *cu_->dex_file);
Bill Buzbeea114add2012-05-03 15:00:40 -0700132 break;
133 case kPseudoMethodExit:
134 LOG(INFO) << "-------- Method_Exit";
135 break;
136 case kPseudoBarrier:
137 LOG(INFO) << "-------- BARRIER";
138 break;
Bill Buzbeea114add2012-05-03 15:00:40 -0700139 case kPseudoEntryBlock:
140 LOG(INFO) << "-------- entry offset: 0x" << std::hex << dest;
141 break;
142 case kPseudoDalvikByteCodeBoundary:
buzbee4ef3e452012-12-14 13:35:28 -0800143 if (lir->operands[0] == 0) {
144 lir->operands[0] = reinterpret_cast<uintptr_t>("No instruction string");
145 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700146 LOG(INFO) << "-------- dalvik offset: 0x" << std::hex
buzbeefa57c472012-11-21 12:06:18 -0800147 << lir->dalvik_offset << " @ " << reinterpret_cast<char*>(lir->operands[0]);
Bill Buzbeea114add2012-05-03 15:00:40 -0700148 break;
149 case kPseudoExitBlock:
150 LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest;
151 break;
152 case kPseudoPseudoAlign4:
buzbeefa57c472012-11-21 12:06:18 -0800153 LOG(INFO) << reinterpret_cast<uintptr_t>(base_addr) + offset << " (0x" << std::hex
Bill Buzbeea114add2012-05-03 15:00:40 -0700154 << offset << "): .align4";
155 break;
156 case kPseudoEHBlockLabel:
157 LOG(INFO) << "Exception_Handling:";
158 break;
159 case kPseudoTargetLabel:
160 case kPseudoNormalBlockLabel:
buzbeecbd6d442012-11-17 14:11:25 -0800161 LOG(INFO) << "L" << reinterpret_cast<void*>(lir) << ":";
Bill Buzbeea114add2012-05-03 15:00:40 -0700162 break;
163 case kPseudoThrowTarget:
buzbeecbd6d442012-11-17 14:11:25 -0800164 LOG(INFO) << "LT" << reinterpret_cast<void*>(lir) << ":";
Bill Buzbeea114add2012-05-03 15:00:40 -0700165 break;
166 case kPseudoIntrinsicRetry:
buzbeecbd6d442012-11-17 14:11:25 -0800167 LOG(INFO) << "IR" << reinterpret_cast<void*>(lir) << ":";
Bill Buzbeea114add2012-05-03 15:00:40 -0700168 break;
169 case kPseudoSuspendTarget:
buzbeecbd6d442012-11-17 14:11:25 -0800170 LOG(INFO) << "LS" << reinterpret_cast<void*>(lir) << ":";
Bill Buzbeea114add2012-05-03 15:00:40 -0700171 break;
buzbee8320f382012-09-11 16:29:42 -0700172 case kPseudoSafepointPC:
buzbeefa57c472012-11-21 12:06:18 -0800173 LOG(INFO) << "LsafepointPC_0x" << std::hex << lir->offset << "_" << lir->dalvik_offset << ":";
buzbee8320f382012-09-11 16:29:42 -0700174 break;
Bill Buzbeea5b30242012-09-28 07:19:44 -0700175 case kPseudoExportedPC:
buzbeefa57c472012-11-21 12:06:18 -0800176 LOG(INFO) << "LexportedPC_0x" << std::hex << lir->offset << "_" << lir->dalvik_offset << ":";
Bill Buzbeea5b30242012-09-28 07:19:44 -0700177 break;
Bill Buzbeea114add2012-05-03 15:00:40 -0700178 case kPseudoCaseLabel:
buzbeecbd6d442012-11-17 14:11:25 -0800179 LOG(INFO) << "LC" << reinterpret_cast<void*>(lir) << ": Case target 0x"
Bill Buzbeea114add2012-05-03 15:00:40 -0700180 << std::hex << lir->operands[0] << "|" << std::dec <<
181 lir->operands[0];
182 break;
183 default:
buzbeefa57c472012-11-21 12:06:18 -0800184 if (lir->flags.is_nop && !dump_nop) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700185 break;
186 } else {
buzbee1fd33462013-03-25 13:40:45 -0700187 std::string op_name(BuildInsnString(GetTargetInstName(lir->opcode),
buzbee02031b12012-11-23 09:41:35 -0800188 lir, base_addr));
buzbee1fd33462013-03-25 13:40:45 -0700189 std::string op_operands(BuildInsnString(GetTargetInstFmt(lir->opcode),
buzbee02031b12012-11-23 09:41:35 -0800190 lir, base_addr));
Bill Buzbeea114add2012-05-03 15:00:40 -0700191 LOG(INFO) << StringPrintf("%05x: %-9s%s%s",
buzbeefa57c472012-11-21 12:06:18 -0800192 reinterpret_cast<unsigned int>(base_addr + offset),
Bill Buzbeea114add2012-05-03 15:00:40 -0700193 op_name.c_str(), op_operands.c_str(),
buzbeefa57c472012-11-21 12:06:18 -0800194 lir->flags.is_nop ? "(nop)" : "");
Bill Buzbeea114add2012-05-03 15:00:40 -0700195 }
196 break;
197 }
buzbee5de34942012-03-01 14:51:57 -0800198
buzbeefa57c472012-11-21 12:06:18 -0800199 if (lir->use_mask && (!lir->flags.is_nop || dump_nop)) {
200 DUMP_RESOURCE_MASK(DumpResourceMask((LIR* ) lir, lir->use_mask, "use"));
Bill Buzbeea114add2012-05-03 15:00:40 -0700201 }
buzbeefa57c472012-11-21 12:06:18 -0800202 if (lir->def_mask && (!lir->flags.is_nop || dump_nop)) {
203 DUMP_RESOURCE_MASK(DumpResourceMask((LIR* ) lir, lir->def_mask, "def"));
Bill Buzbeea114add2012-05-03 15:00:40 -0700204 }
buzbee5de34942012-03-01 14:51:57 -0800205}
206
buzbee1fd33462013-03-25 13:40:45 -0700207void Mir2Lir::DumpPromotionMap()
buzbee5de34942012-03-01 14:51:57 -0800208{
buzbee1fd33462013-03-25 13:40:45 -0700209 int num_regs = cu_->num_dalvik_registers + cu_->num_compiler_temps + 1;
buzbeefa57c472012-11-21 12:06:18 -0800210 for (int i = 0; i < num_regs; i++) {
buzbee1fd33462013-03-25 13:40:45 -0700211 PromotionMap v_reg_map = promotion_map_[i];
Bill Buzbeea114add2012-05-03 15:00:40 -0700212 std::string buf;
buzbeefa57c472012-11-21 12:06:18 -0800213 if (v_reg_map.fp_location == kLocPhysReg) {
buzbee1fd33462013-03-25 13:40:45 -0700214 StringAppendF(&buf, " : s%d", v_reg_map.FpReg & FpRegMask());
buzbee5de34942012-03-01 14:51:57 -0800215 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700216
217 std::string buf3;
buzbee1fd33462013-03-25 13:40:45 -0700218 if (i < cu_->num_dalvik_registers) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700219 StringAppendF(&buf3, "%02d", i);
buzbee1fd33462013-03-25 13:40:45 -0700220 } else if (i == mir_graph_->GetMethodSReg()) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700221 buf3 = "Method*";
222 } else {
buzbee1fd33462013-03-25 13:40:45 -0700223 StringAppendF(&buf3, "ct%d", i - cu_->num_dalvik_registers);
Bill Buzbeea114add2012-05-03 15:00:40 -0700224 }
225
226 LOG(INFO) << StringPrintf("V[%s] -> %s%d%s", buf3.c_str(),
buzbeefa57c472012-11-21 12:06:18 -0800227 v_reg_map.core_location == kLocPhysReg ?
228 "r" : "SP+", v_reg_map.core_location == kLocPhysReg ?
buzbee1fd33462013-03-25 13:40:45 -0700229 v_reg_map.core_reg : SRegOffset(i),
Bill Buzbeea114add2012-05-03 15:00:40 -0700230 buf.c_str());
231 }
buzbee5de34942012-03-01 14:51:57 -0800232}
233
Bill Buzbeea5b30242012-09-28 07:19:44 -0700234/* Dump a mapping table */
buzbee1fd33462013-03-25 13:40:45 -0700235void Mir2Lir::DumpMappingTable(const char* table_name, const std::string& descriptor,
236 const std::string& name, const std::string& signature,
237 const std::vector<uint32_t>& v) {
Bill Buzbeea5b30242012-09-28 07:19:44 -0700238 if (v.size() > 0) {
239 std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name,
240 descriptor.c_str(), name.c_str(), signature.c_str(), v.size()));
241 std::replace(line.begin(), line.end(), ';', '_');
242 LOG(INFO) << line;
243 for (uint32_t i = 0; i < v.size(); i+=2) {
244 line = StringPrintf(" {0x%05x, 0x%04x},", v[i], v[i+1]);
245 LOG(INFO) << line;
246 }
247 LOG(INFO) <<" };\n\n";
248 }
249}
250
buzbee5de34942012-03-01 14:51:57 -0800251/* Dump instructions and constant pool contents */
buzbee1fd33462013-03-25 13:40:45 -0700252void Mir2Lir::CodegenDump()
buzbee5de34942012-03-01 14:51:57 -0800253{
Bill Buzbeea114add2012-05-03 15:00:40 -0700254 LOG(INFO) << "Dumping LIR insns for "
buzbee1fd33462013-03-25 13:40:45 -0700255 << PrettyMethod(cu_->method_idx, *cu_->dex_file);
buzbeefa57c472012-11-21 12:06:18 -0800256 LIR* lir_insn;
buzbee1fd33462013-03-25 13:40:45 -0700257 int insns_size = cu_->code_item->insns_size_in_code_units_;
buzbee5de34942012-03-01 14:51:57 -0800258
buzbee1fd33462013-03-25 13:40:45 -0700259 LOG(INFO) << "Regs (excluding ins) : " << cu_->num_regs;
260 LOG(INFO) << "Ins : " << cu_->num_ins;
261 LOG(INFO) << "Outs : " << cu_->num_outs;
262 LOG(INFO) << "CoreSpills : " << num_core_spills_;
263 LOG(INFO) << "FPSpills : " << num_fp_spills_;
264 LOG(INFO) << "CompilerTemps : " << cu_->num_compiler_temps;
265 LOG(INFO) << "Frame size : " << frame_size_;
266 LOG(INFO) << "code size is " << total_size_ <<
buzbeefa57c472012-11-21 12:06:18 -0800267 " bytes, Dalvik size is " << insns_size * 2;
Bill Buzbeea114add2012-05-03 15:00:40 -0700268 LOG(INFO) << "expansion factor: "
buzbee1fd33462013-03-25 13:40:45 -0700269 << static_cast<float>(total_size_) / static_cast<float>(insns_size * 2);
270 DumpPromotionMap();
271 for (lir_insn = first_lir_insn_; lir_insn != NULL; lir_insn = lir_insn->next) {
272 DumpLIRInsn(lir_insn, 0);
Bill Buzbeea114add2012-05-03 15:00:40 -0700273 }
buzbee1fd33462013-03-25 13:40:45 -0700274 for (lir_insn = literal_list_; lir_insn != NULL; lir_insn = lir_insn->next) {
buzbeefa57c472012-11-21 12:06:18 -0800275 LOG(INFO) << StringPrintf("%x (%04x): .word (%#x)", lir_insn->offset, lir_insn->offset,
276 lir_insn->operands[0]);
Bill Buzbeea114add2012-05-03 15:00:40 -0700277 }
buzbee5de34942012-03-01 14:51:57 -0800278
Bill Buzbeea114add2012-05-03 15:00:40 -0700279 const DexFile::MethodId& method_id =
buzbee1fd33462013-03-25 13:40:45 -0700280 cu_->dex_file->GetMethodId(cu_->method_idx);
281 std::string signature(cu_->dex_file->GetMethodSignature(method_id));
282 std::string name(cu_->dex_file->GetMethodName(method_id));
283 std::string descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id));
buzbee5de34942012-03-01 14:51:57 -0800284
Bill Buzbeea5b30242012-09-28 07:19:44 -0700285 // Dump mapping tables
buzbee1fd33462013-03-25 13:40:45 -0700286 DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, pc2dex_mapping_table_);
287 DumpMappingTable("Dex2PC_MappingTable", descriptor, name, signature, dex2pc_mapping_table_);
buzbee5de34942012-03-01 14:51:57 -0800288}
289
buzbee31a4a6f2012-02-28 15:36:15 -0800290/*
291 * Search the existing constants in the literal pool for an exact or close match
292 * within specified delta (greater or equal to 0).
293 */
buzbee1fd33462013-03-25 13:40:45 -0700294LIR* Mir2Lir::ScanLiteralPool(LIR* data_target, int value, unsigned int delta)
buzbee31a4a6f2012-02-28 15:36:15 -0800295{
buzbeefa57c472012-11-21 12:06:18 -0800296 while (data_target) {
297 if ((static_cast<unsigned>(value - data_target->operands[0])) <= delta)
298 return data_target;
299 data_target = data_target->next;
Bill Buzbeea114add2012-05-03 15:00:40 -0700300 }
301 return NULL;
buzbee31a4a6f2012-02-28 15:36:15 -0800302}
303
304/* Search the existing constants in the literal pool for an exact wide match */
buzbee1fd33462013-03-25 13:40:45 -0700305LIR* Mir2Lir::ScanLiteralPoolWide(LIR* data_target, int val_lo, int val_hi)
buzbee31a4a6f2012-02-28 15:36:15 -0800306{
buzbeefa57c472012-11-21 12:06:18 -0800307 bool lo_match = false;
308 LIR* lo_target = NULL;
309 while (data_target) {
310 if (lo_match && (data_target->operands[0] == val_hi)) {
buzbee4ef3e452012-12-14 13:35:28 -0800311 // Record high word in case we need to expand this later.
312 lo_target->operands[1] = val_hi;
buzbeefa57c472012-11-21 12:06:18 -0800313 return lo_target;
buzbee31a4a6f2012-02-28 15:36:15 -0800314 }
buzbeefa57c472012-11-21 12:06:18 -0800315 lo_match = false;
316 if (data_target->operands[0] == val_lo) {
317 lo_match = true;
318 lo_target = data_target;
Bill Buzbeea114add2012-05-03 15:00:40 -0700319 }
buzbeefa57c472012-11-21 12:06:18 -0800320 data_target = data_target->next;
Bill Buzbeea114add2012-05-03 15:00:40 -0700321 }
322 return NULL;
buzbee31a4a6f2012-02-28 15:36:15 -0800323}
324
325/*
326 * The following are building blocks to insert constants into the pool or
327 * instruction streams.
328 */
329
buzbee4ef3e452012-12-14 13:35:28 -0800330/* Add a 32-bit constant to the constant pool */
buzbee1fd33462013-03-25 13:40:45 -0700331LIR* Mir2Lir::AddWordData(LIR* *constant_list_p, int value)
buzbee31a4a6f2012-02-28 15:36:15 -0800332{
Bill Buzbeea114add2012-05-03 15:00:40 -0700333 /* Add the constant to the literal pool */
buzbeefa57c472012-11-21 12:06:18 -0800334 if (constant_list_p) {
buzbee862a7602013-04-05 10:58:54 -0700335 LIR* new_value = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocData));
buzbeefa57c472012-11-21 12:06:18 -0800336 new_value->operands[0] = value;
337 new_value->next = *constant_list_p;
338 *constant_list_p = new_value;
339 return new_value;
Bill Buzbeea114add2012-05-03 15:00:40 -0700340 }
341 return NULL;
buzbee31a4a6f2012-02-28 15:36:15 -0800342}
343
344/* Add a 64-bit constant to the constant pool or mixed with code */
buzbee1fd33462013-03-25 13:40:45 -0700345LIR* Mir2Lir::AddWideData(LIR* *constant_list_p, int val_lo, int val_hi)
buzbee31a4a6f2012-02-28 15:36:15 -0800346{
buzbee1fd33462013-03-25 13:40:45 -0700347 AddWordData(constant_list_p, val_hi);
348 return AddWordData(constant_list_p, val_lo);
buzbee31a4a6f2012-02-28 15:36:15 -0800349}
350
buzbeeaad94382012-11-21 07:40:50 -0800351static void PushWord(std::vector<uint8_t>&buf, int data) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700352 buf.push_back( data & 0xff);
353 buf.push_back( (data >> 8) & 0xff);
354 buf.push_back( (data >> 16) & 0xff);
355 buf.push_back( (data >> 24) & 0xff);
buzbeee3acd072012-02-25 17:03:10 -0800356}
357
buzbeeaad94382012-11-21 07:40:50 -0800358static void AlignBuffer(std::vector<uint8_t>&buf, size_t offset) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700359 while (buf.size() < offset) {
360 buf.push_back(0);
361 }
buzbeee3acd072012-02-25 17:03:10 -0800362}
363
364/* Write the literal pool to the output stream */
buzbee1fd33462013-03-25 13:40:45 -0700365void Mir2Lir::InstallLiteralPools()
buzbeee3acd072012-02-25 17:03:10 -0800366{
buzbee1fd33462013-03-25 13:40:45 -0700367 AlignBuffer(code_buffer_, data_offset_);
368 LIR* data_lir = literal_list_;
buzbeefa57c472012-11-21 12:06:18 -0800369 while (data_lir != NULL) {
buzbee1fd33462013-03-25 13:40:45 -0700370 PushWord(code_buffer_, data_lir->operands[0]);
buzbeefa57c472012-11-21 12:06:18 -0800371 data_lir = NEXT_LIR(data_lir);
Bill Buzbeea114add2012-05-03 15:00:40 -0700372 }
373 // Push code and method literals, record offsets for the compiler to patch.
buzbee1fd33462013-03-25 13:40:45 -0700374 data_lir = code_literal_list_;
buzbeefa57c472012-11-21 12:06:18 -0800375 while (data_lir != NULL) {
376 uint32_t target = data_lir->operands[0];
buzbee1fd33462013-03-25 13:40:45 -0700377 cu_->compiler_driver->AddCodePatch(cu_->dex_file,
378 cu_->method_idx,
379 cu_->invoke_type,
Ian Rogers1212a022013-03-04 10:48:41 -0800380 target,
381 static_cast<InvokeType>(data_lir->operands[1]),
buzbee1fd33462013-03-25 13:40:45 -0700382 code_buffer_.size());
383 const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
Ian Rogers137e88f2012-10-08 17:46:47 -0700384 // unique based on target to ensure code deduplication works
385 uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
buzbee1fd33462013-03-25 13:40:45 -0700386 PushWord(code_buffer_, unique_patch_value);
buzbeefa57c472012-11-21 12:06:18 -0800387 data_lir = NEXT_LIR(data_lir);
Ian Rogers137e88f2012-10-08 17:46:47 -0700388 }
buzbee1fd33462013-03-25 13:40:45 -0700389 data_lir = method_literal_list_;
buzbeefa57c472012-11-21 12:06:18 -0800390 while (data_lir != NULL) {
391 uint32_t target = data_lir->operands[0];
buzbee1fd33462013-03-25 13:40:45 -0700392 cu_->compiler_driver->AddMethodPatch(cu_->dex_file,
393 cu_->method_idx,
394 cu_->invoke_type,
Ian Rogers1212a022013-03-04 10:48:41 -0800395 target,
396 static_cast<InvokeType>(data_lir->operands[1]),
buzbee1fd33462013-03-25 13:40:45 -0700397 code_buffer_.size());
398 const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
Ian Rogers137e88f2012-10-08 17:46:47 -0700399 // unique based on target to ensure code deduplication works
400 uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
buzbee1fd33462013-03-25 13:40:45 -0700401 PushWord(code_buffer_, unique_patch_value);
buzbeefa57c472012-11-21 12:06:18 -0800402 data_lir = NEXT_LIR(data_lir);
Bill Buzbeea114add2012-05-03 15:00:40 -0700403 }
buzbeee3acd072012-02-25 17:03:10 -0800404}
405
406/* Write the switch tables to the output stream */
buzbee1fd33462013-03-25 13:40:45 -0700407void Mir2Lir::InstallSwitchTables()
buzbeee3acd072012-02-25 17:03:10 -0800408{
buzbee862a7602013-04-05 10:58:54 -0700409 GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
Bill Buzbeea114add2012-05-03 15:00:40 -0700410 while (true) {
buzbee862a7602013-04-05 10:58:54 -0700411 Mir2Lir::SwitchTable* tab_rec = iterator.Next();
buzbeefa57c472012-11-21 12:06:18 -0800412 if (tab_rec == NULL) break;
buzbee1fd33462013-03-25 13:40:45 -0700413 AlignBuffer(code_buffer_, tab_rec->offset);
Bill Buzbeea114add2012-05-03 15:00:40 -0700414 /*
415 * For Arm, our reference point is the address of the bx
416 * instruction that does the launch, so we have to subtract
417 * the auto pc-advance. For other targets the reference point
418 * is a label, so we can use the offset as-is.
419 */
buzbeefa57c472012-11-21 12:06:18 -0800420 int bx_offset = INVALID_OFFSET;
buzbee1fd33462013-03-25 13:40:45 -0700421 switch (cu_->instruction_set) {
buzbeeb046e162012-10-30 15:48:42 -0700422 case kThumb2:
buzbeefa57c472012-11-21 12:06:18 -0800423 bx_offset = tab_rec->anchor->offset + 4;
buzbeeb046e162012-10-30 15:48:42 -0700424 break;
425 case kX86:
buzbeefa57c472012-11-21 12:06:18 -0800426 bx_offset = 0;
buzbeeb046e162012-10-30 15:48:42 -0700427 break;
428 case kMips:
buzbeefa57c472012-11-21 12:06:18 -0800429 bx_offset = tab_rec->anchor->offset;
buzbeeb046e162012-10-30 15:48:42 -0700430 break;
buzbee1fd33462013-03-25 13:40:45 -0700431 default: LOG(FATAL) << "Unexpected instruction set: " << cu_->instruction_set;
buzbeeb046e162012-10-30 15:48:42 -0700432 }
buzbee1fd33462013-03-25 13:40:45 -0700433 if (cu_->verbose) {
buzbeefa57c472012-11-21 12:06:18 -0800434 LOG(INFO) << "Switch table for offset 0x" << std::hex << bx_offset;
buzbeee3acd072012-02-25 17:03:10 -0800435 }
buzbeefa57c472012-11-21 12:06:18 -0800436 if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
437 const int* keys = reinterpret_cast<const int*>(&(tab_rec->table[2]));
438 for (int elems = 0; elems < tab_rec->table[1]; elems++) {
439 int disp = tab_rec->targets[elems]->offset - bx_offset;
buzbee1fd33462013-03-25 13:40:45 -0700440 if (cu_->verbose) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700441 LOG(INFO) << " Case[" << elems << "] key: 0x"
442 << std::hex << keys[elems] << ", disp: 0x"
443 << std::hex << disp;
444 }
buzbee1fd33462013-03-25 13:40:45 -0700445 PushWord(code_buffer_, keys[elems]);
446 PushWord(code_buffer_,
buzbeefa57c472012-11-21 12:06:18 -0800447 tab_rec->targets[elems]->offset - bx_offset);
Bill Buzbeea114add2012-05-03 15:00:40 -0700448 }
449 } else {
buzbeefa57c472012-11-21 12:06:18 -0800450 DCHECK_EQ(static_cast<int>(tab_rec->table[0]),
Bill Buzbeea114add2012-05-03 15:00:40 -0700451 static_cast<int>(Instruction::kPackedSwitchSignature));
buzbeefa57c472012-11-21 12:06:18 -0800452 for (int elems = 0; elems < tab_rec->table[1]; elems++) {
453 int disp = tab_rec->targets[elems]->offset - bx_offset;
buzbee1fd33462013-03-25 13:40:45 -0700454 if (cu_->verbose) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700455 LOG(INFO) << " Case[" << elems << "] disp: 0x"
456 << std::hex << disp;
457 }
buzbee1fd33462013-03-25 13:40:45 -0700458 PushWord(code_buffer_, tab_rec->targets[elems]->offset - bx_offset);
Bill Buzbeea114add2012-05-03 15:00:40 -0700459 }
460 }
461 }
buzbeee3acd072012-02-25 17:03:10 -0800462}
463
464/* Write the fill array dta to the output stream */
buzbee1fd33462013-03-25 13:40:45 -0700465void Mir2Lir::InstallFillArrayData()
buzbeee3acd072012-02-25 17:03:10 -0800466{
buzbee862a7602013-04-05 10:58:54 -0700467 GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
Bill Buzbeea114add2012-05-03 15:00:40 -0700468 while (true) {
buzbee862a7602013-04-05 10:58:54 -0700469 Mir2Lir::FillArrayData *tab_rec = iterator.Next();
buzbeefa57c472012-11-21 12:06:18 -0800470 if (tab_rec == NULL) break;
buzbee1fd33462013-03-25 13:40:45 -0700471 AlignBuffer(code_buffer_, tab_rec->offset);
buzbeefa57c472012-11-21 12:06:18 -0800472 for (int i = 0; i < (tab_rec->size + 1) / 2; i++) {
buzbee1fd33462013-03-25 13:40:45 -0700473 code_buffer_.push_back( tab_rec->table[i] & 0xFF);
474 code_buffer_.push_back( (tab_rec->table[i] >> 8) & 0xFF);
buzbeee3acd072012-02-25 17:03:10 -0800475 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700476 }
buzbeee3acd072012-02-25 17:03:10 -0800477}
478
buzbeeaad94382012-11-21 07:40:50 -0800479static int AssignLiteralOffsetCommon(LIR* lir, int offset)
buzbeee3acd072012-02-25 17:03:10 -0800480{
Bill Buzbeea114add2012-05-03 15:00:40 -0700481 for (;lir != NULL; lir = lir->next) {
482 lir->offset = offset;
483 offset += 4;
484 }
485 return offset;
buzbeee3acd072012-02-25 17:03:10 -0800486}
487
buzbee6459e7c2012-10-02 14:42:41 -0700488// Make sure we have a code address for every declared catch entry
buzbee1fd33462013-03-25 13:40:45 -0700489bool Mir2Lir::VerifyCatchEntries()
buzbee6459e7c2012-10-02 14:42:41 -0700490{
491 bool success = true;
buzbee1fd33462013-03-25 13:40:45 -0700492 for (std::set<uint32_t>::const_iterator it = mir_graph_->catches_.begin();
493 it != mir_graph_->catches_.end(); ++it) {
buzbeefa57c472012-11-21 12:06:18 -0800494 uint32_t dex_pc = *it;
buzbee6459e7c2012-10-02 14:42:41 -0700495 bool found = false;
buzbee1fd33462013-03-25 13:40:45 -0700496 for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) {
497 if (dex_pc == dex2pc_mapping_table_[i+1]) {
buzbee6459e7c2012-10-02 14:42:41 -0700498 found = true;
499 break;
500 }
501 }
502 if (!found) {
buzbeefa57c472012-11-21 12:06:18 -0800503 LOG(INFO) << "Missing native PC for catch entry @ 0x" << std::hex << dex_pc;
buzbee6459e7c2012-10-02 14:42:41 -0700504 success = false;
505 }
506 }
507 // Now, try in the other direction
buzbee1fd33462013-03-25 13:40:45 -0700508 for (size_t i = 0; i < dex2pc_mapping_table_.size(); i += 2) {
509 uint32_t dex_pc = dex2pc_mapping_table_[i+1];
510 if (mir_graph_->catches_.find(dex_pc) == mir_graph_->catches_.end()) {
buzbeefa57c472012-11-21 12:06:18 -0800511 LOG(INFO) << "Unexpected catch entry @ dex pc 0x" << std::hex << dex_pc;
buzbee6459e7c2012-10-02 14:42:41 -0700512 success = false;
513 }
514 }
515 if (!success) {
buzbee1fd33462013-03-25 13:40:45 -0700516 LOG(INFO) << "Bad dex2pcMapping table in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
517 LOG(INFO) << "Entries @ decode: " << mir_graph_->catches_.size() << ", Entries in table: "
518 << dex2pc_mapping_table_.size()/2;
buzbee6459e7c2012-10-02 14:42:41 -0700519 }
520 return success;
521}
522
buzbee311ca162013-02-28 15:56:43 -0800523
buzbee1fd33462013-03-25 13:40:45 -0700524void Mir2Lir::CreateMappingTables()
buzbeee3acd072012-02-25 17:03:10 -0800525{
buzbee1fd33462013-03-25 13:40:45 -0700526 for (LIR* tgt_lir = first_lir_insn_; tgt_lir != NULL; tgt_lir = NEXT_LIR(tgt_lir)) {
buzbeefa57c472012-11-21 12:06:18 -0800527 if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) {
buzbee1fd33462013-03-25 13:40:45 -0700528 pc2dex_mapping_table_.push_back(tgt_lir->offset);
529 pc2dex_mapping_table_.push_back(tgt_lir->dalvik_offset);
Bill Buzbeea5b30242012-09-28 07:19:44 -0700530 }
buzbeefa57c472012-11-21 12:06:18 -0800531 if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoExportedPC)) {
buzbee1fd33462013-03-25 13:40:45 -0700532 dex2pc_mapping_table_.push_back(tgt_lir->offset);
533 dex2pc_mapping_table_.push_back(tgt_lir->dalvik_offset);
buzbeee3acd072012-02-25 17:03:10 -0800534 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700535 }
buzbee311ca162013-02-28 15:56:43 -0800536 if (kIsDebugBuild) {
buzbee1fd33462013-03-25 13:40:45 -0700537 DCHECK(VerifyCatchEntries());
buzbee311ca162013-02-28 15:56:43 -0800538 }
buzbee1fd33462013-03-25 13:40:45 -0700539 combined_mapping_table_.push_back(pc2dex_mapping_table_.size() +
540 dex2pc_mapping_table_.size());
541 combined_mapping_table_.push_back(pc2dex_mapping_table_.size());
542 combined_mapping_table_.insert(combined_mapping_table_.end(), pc2dex_mapping_table_.begin(),
543 pc2dex_mapping_table_.end());
544 combined_mapping_table_.insert(combined_mapping_table_.end(), dex2pc_mapping_table_.begin(),
545 dex2pc_mapping_table_.end());
buzbeee3acd072012-02-25 17:03:10 -0800546}
547
Ian Rogers0c7abda2012-09-19 13:33:42 -0700548class NativePcToReferenceMapBuilder {
549 public:
550 NativePcToReferenceMapBuilder(std::vector<uint8_t>* table,
551 size_t entries, uint32_t max_native_offset,
552 size_t references_width) : entries_(entries),
553 references_width_(references_width), in_use_(entries),
554 table_(table) {
555 // Compute width in bytes needed to hold max_native_offset.
556 native_offset_width_ = 0;
557 while (max_native_offset != 0) {
558 native_offset_width_++;
559 max_native_offset >>= 8;
560 }
561 // Resize table and set up header.
562 table->resize((EntryWidth() * entries) + sizeof(uint32_t));
Ian Rogers000d7242012-09-21 16:07:36 -0700563 CHECK_LT(native_offset_width_, 1U << 3);
564 (*table)[0] = native_offset_width_ & 7;
565 CHECK_LT(references_width_, 1U << 13);
566 (*table)[0] |= (references_width_ << 3) & 0xFF;
567 (*table)[1] = (references_width_ >> 5) & 0xFF;
Ian Rogers0c7abda2012-09-19 13:33:42 -0700568 CHECK_LT(entries, 1U << 16);
569 (*table)[2] = entries & 0xFF;
570 (*table)[3] = (entries >> 8) & 0xFF;
571 }
572
573 void AddEntry(uint32_t native_offset, const uint8_t* references) {
574 size_t table_index = TableIndex(native_offset);
575 while (in_use_[table_index]) {
576 table_index = (table_index + 1) % entries_;
577 }
578 in_use_[table_index] = true;
579 SetNativeOffset(table_index, native_offset);
580 DCHECK_EQ(native_offset, GetNativeOffset(table_index));
581 SetReferences(table_index, references);
582 }
583
584 private:
585 size_t TableIndex(uint32_t native_offset) {
586 return NativePcOffsetToReferenceMap::Hash(native_offset) % entries_;
587 }
588
589 uint32_t GetNativeOffset(size_t table_index) {
590 uint32_t native_offset = 0;
591 size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
592 for (size_t i = 0; i < native_offset_width_; i++) {
593 native_offset |= (*table_)[table_offset + i] << (i * 8);
594 }
595 return native_offset;
596 }
597
598 void SetNativeOffset(size_t table_index, uint32_t native_offset) {
599 size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
600 for (size_t i = 0; i < native_offset_width_; i++) {
601 (*table_)[table_offset + i] = (native_offset >> (i * 8)) & 0xFF;
602 }
603 }
604
605 void SetReferences(size_t table_index, const uint8_t* references) {
606 size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
607 memcpy(&(*table_)[table_offset + native_offset_width_], references, references_width_);
608 }
609
610 size_t EntryWidth() const {
611 return native_offset_width_ + references_width_;
612 }
613
614 // Number of entries in the table.
615 const size_t entries_;
616 // Number of bytes used to encode the reference bitmap.
617 const size_t references_width_;
618 // Number of bytes used to encode a native offset.
619 size_t native_offset_width_;
620 // Entries that are in use.
621 std::vector<bool> in_use_;
622 // The table we're building.
623 std::vector<uint8_t>* const table_;
624};
625
buzbee1fd33462013-03-25 13:40:45 -0700626void Mir2Lir::CreateNativeGcMap() {
627 const std::vector<uint32_t>& mapping_table = pc2dex_mapping_table_;
Ian Rogers0c7abda2012-09-19 13:33:42 -0700628 uint32_t max_native_offset = 0;
629 for (size_t i = 0; i < mapping_table.size(); i += 2) {
630 uint32_t native_offset = mapping_table[i + 0];
631 if (native_offset > max_native_offset) {
632 max_native_offset = native_offset;
633 }
634 }
Brian Carlstrom51c24672013-07-11 16:00:56 -0700635 MethodReference method_ref(cu_->dex_file, cu_->method_idx);
Ian Rogers0c7abda2012-09-19 13:33:42 -0700636 const std::vector<uint8_t>* gc_map_raw = verifier::MethodVerifier::GetDexGcMap(method_ref);
637 verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[4], gc_map_raw->size() - 4);
638 // Compute native offset to references size.
buzbee1fd33462013-03-25 13:40:45 -0700639 NativePcToReferenceMapBuilder native_gc_map_builder(&native_gc_map_,
Ian Rogers0c7abda2012-09-19 13:33:42 -0700640 mapping_table.size() / 2, max_native_offset,
641 dex_gc_map.RegWidth());
642
643 for (size_t i = 0; i < mapping_table.size(); i += 2) {
644 uint32_t native_offset = mapping_table[i + 0];
645 uint32_t dex_pc = mapping_table[i + 1];
646 const uint8_t* references = dex_gc_map.FindBitMap(dex_pc, false);
Bill Buzbeea5b30242012-09-28 07:19:44 -0700647 CHECK(references != NULL) << "Missing ref for dex pc 0x" << std::hex << dex_pc;
648 native_gc_map_builder.AddEntry(native_offset, references);
Ian Rogers0c7abda2012-09-19 13:33:42 -0700649 }
650}
651
buzbeee3acd072012-02-25 17:03:10 -0800652/* Determine the offset of each literal field */
buzbee1fd33462013-03-25 13:40:45 -0700653int Mir2Lir::AssignLiteralOffset(int offset)
buzbeee3acd072012-02-25 17:03:10 -0800654{
buzbee1fd33462013-03-25 13:40:45 -0700655 offset = AssignLiteralOffsetCommon(literal_list_, offset);
656 offset = AssignLiteralOffsetCommon(code_literal_list_, offset);
657 offset = AssignLiteralOffsetCommon(method_literal_list_, offset);
Bill Buzbeea114add2012-05-03 15:00:40 -0700658 return offset;
buzbeee3acd072012-02-25 17:03:10 -0800659}
660
buzbee1fd33462013-03-25 13:40:45 -0700661int Mir2Lir::AssignSwitchTablesOffset(int offset)
buzbeee3acd072012-02-25 17:03:10 -0800662{
buzbee862a7602013-04-05 10:58:54 -0700663 GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
Bill Buzbeea114add2012-05-03 15:00:40 -0700664 while (true) {
buzbee862a7602013-04-05 10:58:54 -0700665 Mir2Lir::SwitchTable *tab_rec = iterator.Next();
buzbeefa57c472012-11-21 12:06:18 -0800666 if (tab_rec == NULL) break;
667 tab_rec->offset = offset;
668 if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
669 offset += tab_rec->table[1] * (sizeof(int) * 2);
Bill Buzbeea114add2012-05-03 15:00:40 -0700670 } else {
buzbeefa57c472012-11-21 12:06:18 -0800671 DCHECK_EQ(static_cast<int>(tab_rec->table[0]),
Bill Buzbeea114add2012-05-03 15:00:40 -0700672 static_cast<int>(Instruction::kPackedSwitchSignature));
buzbeefa57c472012-11-21 12:06:18 -0800673 offset += tab_rec->table[1] * sizeof(int);
buzbeee3acd072012-02-25 17:03:10 -0800674 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700675 }
676 return offset;
buzbeee3acd072012-02-25 17:03:10 -0800677}
678
buzbee1fd33462013-03-25 13:40:45 -0700679int Mir2Lir::AssignFillArrayDataOffset(int offset)
buzbeee3acd072012-02-25 17:03:10 -0800680{
buzbee862a7602013-04-05 10:58:54 -0700681 GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
Bill Buzbeea114add2012-05-03 15:00:40 -0700682 while (true) {
buzbee862a7602013-04-05 10:58:54 -0700683 Mir2Lir::FillArrayData *tab_rec = iterator.Next();
buzbeefa57c472012-11-21 12:06:18 -0800684 if (tab_rec == NULL) break;
685 tab_rec->offset = offset;
686 offset += tab_rec->size;
Bill Buzbeea114add2012-05-03 15:00:40 -0700687 // word align
688 offset = (offset + 3) & ~3;
689 }
690 return offset;
buzbeee3acd072012-02-25 17:03:10 -0800691}
692
buzbeea3a82b22012-11-27 16:09:55 -0800693// LIR offset assignment.
buzbee1fd33462013-03-25 13:40:45 -0700694int Mir2Lir::AssignInsnOffsets()
buzbeea3a82b22012-11-27 16:09:55 -0800695{
696 LIR* lir;
697 int offset = 0;
698
buzbee1fd33462013-03-25 13:40:45 -0700699 for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
buzbeea3a82b22012-11-27 16:09:55 -0800700 lir->offset = offset;
701 if (lir->opcode >= 0) {
702 if (!lir->flags.is_nop) {
703 offset += lir->flags.size;
704 }
705 } else if (lir->opcode == kPseudoPseudoAlign4) {
706 if (offset & 0x2) {
707 offset += 2;
708 lir->operands[0] = 1;
709 } else {
710 lir->operands[0] = 0;
711 }
712 }
713 /* Pseudo opcodes don't consume space */
714 }
715
716 return offset;
717}
718
buzbeee3acd072012-02-25 17:03:10 -0800719/*
720 * Walk the compilation unit and assign offsets to instructions
721 * and literals and compute the total size of the compiled unit.
722 */
buzbee1fd33462013-03-25 13:40:45 -0700723void Mir2Lir::AssignOffsets()
buzbeee3acd072012-02-25 17:03:10 -0800724{
buzbee1fd33462013-03-25 13:40:45 -0700725 int offset = AssignInsnOffsets();
buzbeee3acd072012-02-25 17:03:10 -0800726
Bill Buzbeea114add2012-05-03 15:00:40 -0700727 /* Const values have to be word aligned */
728 offset = (offset + 3) & ~3;
buzbeee3acd072012-02-25 17:03:10 -0800729
Bill Buzbeea114add2012-05-03 15:00:40 -0700730 /* Set up offsets for literals */
buzbee1fd33462013-03-25 13:40:45 -0700731 data_offset_ = offset;
buzbeee3acd072012-02-25 17:03:10 -0800732
buzbee1fd33462013-03-25 13:40:45 -0700733 offset = AssignLiteralOffset(offset);
buzbeee3acd072012-02-25 17:03:10 -0800734
buzbee1fd33462013-03-25 13:40:45 -0700735 offset = AssignSwitchTablesOffset(offset);
buzbeee3acd072012-02-25 17:03:10 -0800736
buzbee1fd33462013-03-25 13:40:45 -0700737 offset = AssignFillArrayDataOffset(offset);
buzbeee3acd072012-02-25 17:03:10 -0800738
buzbee1fd33462013-03-25 13:40:45 -0700739 total_size_ = offset;
buzbeee3acd072012-02-25 17:03:10 -0800740}
741
742/*
743 * Go over each instruction in the list and calculate the offset from the top
744 * before sending them off to the assembler. If out-of-range branch distance is
745 * seen rearrange the instructions a bit to correct it.
746 */
buzbee1fd33462013-03-25 13:40:45 -0700747void Mir2Lir::AssembleLIR()
buzbeee3acd072012-02-25 17:03:10 -0800748{
buzbee1fd33462013-03-25 13:40:45 -0700749 AssignOffsets();
buzbee311ca162013-02-28 15:56:43 -0800750 int assembler_retries = 0;
Bill Buzbeea114add2012-05-03 15:00:40 -0700751 /*
752 * Assemble here. Note that we generate code with optimistic assumptions
753 * and if found now to work, we'll have to redo the sequence and retry.
754 */
buzbeee3acd072012-02-25 17:03:10 -0800755
Bill Buzbeea114add2012-05-03 15:00:40 -0700756 while (true) {
buzbee1fd33462013-03-25 13:40:45 -0700757 AssemblerStatus res = AssembleInstructions(0);
Bill Buzbeea114add2012-05-03 15:00:40 -0700758 if (res == kSuccess) {
759 break;
760 } else {
buzbee311ca162013-02-28 15:56:43 -0800761 assembler_retries++;
762 if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
buzbee1fd33462013-03-25 13:40:45 -0700763 CodegenDump();
Bill Buzbeea114add2012-05-03 15:00:40 -0700764 LOG(FATAL) << "Assembler error - too many retries";
765 }
766 // Redo offsets and try again
buzbee1fd33462013-03-25 13:40:45 -0700767 AssignOffsets();
768 code_buffer_.clear();
buzbeee3acd072012-02-25 17:03:10 -0800769 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700770 }
buzbeee3acd072012-02-25 17:03:10 -0800771
Bill Buzbeea114add2012-05-03 15:00:40 -0700772 // Install literals
buzbee1fd33462013-03-25 13:40:45 -0700773 InstallLiteralPools();
buzbeee3acd072012-02-25 17:03:10 -0800774
Bill Buzbeea114add2012-05-03 15:00:40 -0700775 // Install switch tables
buzbee1fd33462013-03-25 13:40:45 -0700776 InstallSwitchTables();
buzbeee3acd072012-02-25 17:03:10 -0800777
Bill Buzbeea114add2012-05-03 15:00:40 -0700778 // Install fill array data
buzbee1fd33462013-03-25 13:40:45 -0700779 InstallFillArrayData();
buzbeee3acd072012-02-25 17:03:10 -0800780
Ian Rogers0c7abda2012-09-19 13:33:42 -0700781 // Create the mapping table and native offset to reference map.
buzbee1fd33462013-03-25 13:40:45 -0700782 CreateMappingTables();
Ian Rogers0c7abda2012-09-19 13:33:42 -0700783
buzbee1fd33462013-03-25 13:40:45 -0700784 CreateNativeGcMap();
buzbeee3acd072012-02-25 17:03:10 -0800785}
786
buzbee31a4a6f2012-02-28 15:36:15 -0800787/*
788 * Insert a kPseudoCaseLabel at the beginning of the Dalvik
789 * offset vaddr. This label will be used to fix up the case
790 * branch table during the assembly phase. Be sure to set
791 * all resource flags on this to prevent code motion across
792 * target boundaries. KeyVal is just there for debugging.
793 */
buzbee1fd33462013-03-25 13:40:45 -0700794LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal)
buzbee31a4a6f2012-02-28 15:36:15 -0800795{
Bill Buzbeea114add2012-05-03 15:00:40 -0700796 SafeMap<unsigned int, LIR*>::iterator it;
buzbee1fd33462013-03-25 13:40:45 -0700797 it = boundary_map_.find(vaddr);
798 if (it == boundary_map_.end()) {
Bill Buzbeea114add2012-05-03 15:00:40 -0700799 LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
800 }
buzbee862a7602013-04-05 10:58:54 -0700801 LIR* new_label = static_cast<LIR*>(arena_->NewMem(sizeof(LIR), true, ArenaAllocator::kAllocLIR));
buzbeefa57c472012-11-21 12:06:18 -0800802 new_label->dalvik_offset = vaddr;
803 new_label->opcode = kPseudoCaseLabel;
804 new_label->operands[0] = keyVal;
805 InsertLIRAfter(it->second, new_label);
806 return new_label;
buzbee31a4a6f2012-02-28 15:36:15 -0800807}
808
buzbee1fd33462013-03-25 13:40:45 -0700809void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec)
buzbee31a4a6f2012-02-28 15:36:15 -0800810{
buzbeefa57c472012-11-21 12:06:18 -0800811 const uint16_t* table = tab_rec->table;
812 int base_vaddr = tab_rec->vaddr;
buzbeecbd6d442012-11-17 14:11:25 -0800813 const int *targets = reinterpret_cast<const int*>(&table[4]);
Bill Buzbeea114add2012-05-03 15:00:40 -0700814 int entries = table[1];
buzbeefa57c472012-11-21 12:06:18 -0800815 int low_key = s4FromSwitchData(&table[2]);
Bill Buzbeea114add2012-05-03 15:00:40 -0700816 for (int i = 0; i < entries; i++) {
buzbee1fd33462013-03-25 13:40:45 -0700817 tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], i + low_key);
Bill Buzbeea114add2012-05-03 15:00:40 -0700818 }
buzbee31a4a6f2012-02-28 15:36:15 -0800819}
820
buzbee1fd33462013-03-25 13:40:45 -0700821void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec)
buzbee31a4a6f2012-02-28 15:36:15 -0800822{
buzbeefa57c472012-11-21 12:06:18 -0800823 const uint16_t* table = tab_rec->table;
824 int base_vaddr = tab_rec->vaddr;
Bill Buzbeea114add2012-05-03 15:00:40 -0700825 int entries = table[1];
buzbeecbd6d442012-11-17 14:11:25 -0800826 const int* keys = reinterpret_cast<const int*>(&table[2]);
827 const int* targets = &keys[entries];
Bill Buzbeea114add2012-05-03 15:00:40 -0700828 for (int i = 0; i < entries; i++) {
buzbee1fd33462013-03-25 13:40:45 -0700829 tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], keys[i]);
Bill Buzbeea114add2012-05-03 15:00:40 -0700830 }
buzbee31a4a6f2012-02-28 15:36:15 -0800831}
832
buzbee1fd33462013-03-25 13:40:45 -0700833void Mir2Lir::ProcessSwitchTables()
buzbee31a4a6f2012-02-28 15:36:15 -0800834{
buzbee862a7602013-04-05 10:58:54 -0700835 GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
Bill Buzbeea114add2012-05-03 15:00:40 -0700836 while (true) {
buzbee862a7602013-04-05 10:58:54 -0700837 Mir2Lir::SwitchTable *tab_rec = iterator.Next();
buzbeefa57c472012-11-21 12:06:18 -0800838 if (tab_rec == NULL) break;
839 if (tab_rec->table[0] == Instruction::kPackedSwitchSignature) {
buzbee1fd33462013-03-25 13:40:45 -0700840 MarkPackedCaseLabels(tab_rec);
buzbeefa57c472012-11-21 12:06:18 -0800841 } else if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
buzbee1fd33462013-03-25 13:40:45 -0700842 MarkSparseCaseLabels(tab_rec);
Bill Buzbeea114add2012-05-03 15:00:40 -0700843 } else {
844 LOG(FATAL) << "Invalid switch table";
buzbee31a4a6f2012-02-28 15:36:15 -0800845 }
Bill Buzbeea114add2012-05-03 15:00:40 -0700846 }
buzbee31a4a6f2012-02-28 15:36:15 -0800847}
848
buzbee1fd33462013-03-25 13:40:45 -0700849void Mir2Lir::DumpSparseSwitchTable(const uint16_t* table)
Bill Buzbeea114add2012-05-03 15:00:40 -0700850 /*
851 * Sparse switch data format:
852 * ushort ident = 0x0200 magic value
853 * ushort size number of entries in the table; > 0
854 * int keys[size] keys, sorted low-to-high; 32-bit aligned
855 * int targets[size] branch targets, relative to switch opcode
856 *
857 * Total size is (2+size*4) 16-bit code units.
858 */
buzbee31a4a6f2012-02-28 15:36:15 -0800859{
buzbeeeaf09bc2012-11-15 14:51:41 -0800860 uint16_t ident = table[0];
Bill Buzbeea114add2012-05-03 15:00:40 -0700861 int entries = table[1];
buzbeecbd6d442012-11-17 14:11:25 -0800862 const int* keys = reinterpret_cast<const int*>(&table[2]);
863 const int* targets = &keys[entries];
Bill Buzbeea114add2012-05-03 15:00:40 -0700864 LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident
865 << ", entries: " << std::dec << entries;
866 for (int i = 0; i < entries; i++) {
867 LOG(INFO) << " Key[" << keys[i] << "] -> 0x" << std::hex << targets[i];
868 }
buzbee31a4a6f2012-02-28 15:36:15 -0800869}
870
buzbee1fd33462013-03-25 13:40:45 -0700871void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table)
Bill Buzbeea114add2012-05-03 15:00:40 -0700872 /*
873 * Packed switch data format:
874 * ushort ident = 0x0100 magic value
875 * ushort size number of entries in the table
876 * int first_key first (and lowest) switch case value
877 * int targets[size] branch targets, relative to switch opcode
878 *
879 * Total size is (4+size*2) 16-bit code units.
880 */
buzbee31a4a6f2012-02-28 15:36:15 -0800881{
buzbeeeaf09bc2012-11-15 14:51:41 -0800882 uint16_t ident = table[0];
buzbeecbd6d442012-11-17 14:11:25 -0800883 const int* targets = reinterpret_cast<const int*>(&table[4]);
Bill Buzbeea114add2012-05-03 15:00:40 -0700884 int entries = table[1];
buzbeefa57c472012-11-21 12:06:18 -0800885 int low_key = s4FromSwitchData(&table[2]);
Bill Buzbeea114add2012-05-03 15:00:40 -0700886 LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident
buzbeefa57c472012-11-21 12:06:18 -0800887 << ", entries: " << std::dec << entries << ", low_key: " << low_key;
Bill Buzbeea114add2012-05-03 15:00:40 -0700888 for (int i = 0; i < entries; i++) {
buzbeefa57c472012-11-21 12:06:18 -0800889 LOG(INFO) << " Key[" << (i + low_key) << "] -> 0x" << std::hex
Bill Buzbeea114add2012-05-03 15:00:40 -0700890 << targets[i];
891 }
buzbee31a4a6f2012-02-28 15:36:15 -0800892}
buzbeee3acd072012-02-25 17:03:10 -0800893
buzbeed1643e42012-09-05 14:06:51 -0700894/*
895 * Set up special LIR to mark a Dalvik byte-code instruction start and
buzbeefa57c472012-11-21 12:06:18 -0800896 * record it in the boundary_map. NOTE: in cases such as kMirOpCheck in
buzbeed1643e42012-09-05 14:06:51 -0700897 * which we split a single Dalvik instruction, only the first MIR op
898 * associated with a Dalvik PC should be entered into the map.
899 */
buzbee1fd33462013-03-25 13:40:45 -0700900LIR* Mir2Lir::MarkBoundary(int offset, const char* inst_str)
buzbeed1643e42012-09-05 14:06:51 -0700901{
buzbee1fd33462013-03-25 13:40:45 -0700902 LIR* res = NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast<uintptr_t>(inst_str));
903 if (boundary_map_.find(offset) == boundary_map_.end()) {
904 boundary_map_.Put(offset, res);
buzbeed1643e42012-09-05 14:06:51 -0700905 }
906 return res;
907}
buzbeee3acd072012-02-25 17:03:10 -0800908
buzbee1fd33462013-03-25 13:40:45 -0700909bool Mir2Lir::EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2)
buzbeee6285f92012-12-06 15:57:46 -0800910{
911 bool is_taken;
912 switch (opcode) {
913 case Instruction::IF_EQ: is_taken = (src1 == src2); break;
914 case Instruction::IF_NE: is_taken = (src1 != src2); break;
915 case Instruction::IF_LT: is_taken = (src1 < src2); break;
916 case Instruction::IF_GE: is_taken = (src1 >= src2); break;
917 case Instruction::IF_GT: is_taken = (src1 > src2); break;
918 case Instruction::IF_LE: is_taken = (src1 <= src2); break;
919 case Instruction::IF_EQZ: is_taken = (src1 == 0); break;
920 case Instruction::IF_NEZ: is_taken = (src1 != 0); break;
921 case Instruction::IF_LTZ: is_taken = (src1 < 0); break;
922 case Instruction::IF_GEZ: is_taken = (src1 >= 0); break;
923 case Instruction::IF_GTZ: is_taken = (src1 > 0); break;
924 case Instruction::IF_LEZ: is_taken = (src1 <= 0); break;
925 default:
926 LOG(FATAL) << "Unexpected opcode " << opcode;
927 is_taken = false;
928 }
929 return is_taken;
930}
931
buzbee4ef3e452012-12-14 13:35:28 -0800932// Convert relation of src1/src2 to src2/src1
buzbee1fd33462013-03-25 13:40:45 -0700933ConditionCode Mir2Lir::FlipComparisonOrder(ConditionCode before) {
buzbee4ef3e452012-12-14 13:35:28 -0800934 ConditionCode res;
935 switch (before) {
936 case kCondEq: res = kCondEq; break;
937 case kCondNe: res = kCondNe; break;
938 case kCondLt: res = kCondGt; break;
939 case kCondGt: res = kCondLt; break;
940 case kCondLe: res = kCondGe; break;
941 case kCondGe: res = kCondLe; break;
942 default:
943 res = static_cast<ConditionCode>(0);
944 LOG(FATAL) << "Unexpected ccode " << before;
945 }
946 return res;
947}
948
buzbee862a7602013-04-05 10:58:54 -0700949// TODO: move to mir_to_lir.cc
950Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena)
Ian Rogers6282dc12013-04-18 15:54:02 -0700951 : Backend(arena),
952 literal_list_(NULL),
buzbee1fd33462013-03-25 13:40:45 -0700953 method_literal_list_(NULL),
954 code_literal_list_(NULL),
955 cu_(cu),
956 mir_graph_(mir_graph),
buzbee862a7602013-04-05 10:58:54 -0700957 switch_tables_(arena, 4, kGrowableArraySwitchTables),
958 fill_array_data_(arena, 4, kGrowableArrayFillArrayData),
959 throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads),
960 suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads),
961 intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc),
buzbee1fd33462013-03-25 13:40:45 -0700962 data_offset_(0),
963 total_size_(0),
964 block_label_list_(NULL),
buzbee862a7602013-04-05 10:58:54 -0700965 current_dalvik_offset_(0),
966 reg_pool_(NULL),
buzbee1fd33462013-03-25 13:40:45 -0700967 live_sreg_(0),
968 num_core_spills_(0),
969 num_fp_spills_(0),
970 frame_size_(0),
971 core_spill_mask_(0),
972 fp_spill_mask_(0),
973 first_lir_insn_(NULL),
974 last_lir_insn_(NULL)
975 {
buzbee1fd33462013-03-25 13:40:45 -0700976 promotion_map_ = static_cast<PromotionMap*>
buzbee862a7602013-04-05 10:58:54 -0700977 (arena_->NewMem((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) *
978 sizeof(promotion_map_[0]), true, ArenaAllocator::kAllocRegAlloc));
buzbee1fd33462013-03-25 13:40:45 -0700979}
980
981void Mir2Lir::Materialize() {
982 CompilerInitializeRegAlloc(); // Needs to happen after SSA naming
983
984 /* Allocate Registers using simple local allocation scheme */
985 SimpleRegAlloc();
986
987 //FIXME: re-enable by retrieving from mir_graph
988 SpecialCaseHandler special_case = kNoHandler;
989
990 if (special_case != kNoHandler) {
991 /*
992 * Custom codegen for special cases. If for any reason the
993 * special codegen doesn't succeed, first_lir_insn_ will
994 * set to NULL;
995 */
996 SpecialMIR2LIR(special_case);
997 }
998
999 /* Convert MIR to LIR, etc. */
1000 if (first_lir_insn_ == NULL) {
1001 MethodMIR2LIR();
1002 }
1003
1004 /* Method is not empty */
1005 if (first_lir_insn_) {
1006
1007 // mark the targets of switch statement case labels
1008 ProcessSwitchTables();
1009
1010 /* Convert LIR into machine code. */
1011 AssembleLIR();
1012
1013 if (cu_->verbose) {
1014 CodegenDump();
1015 }
1016
1017 }
1018
1019}
1020
1021CompiledMethod* Mir2Lir::GetCompiledMethod() {
1022 // Combine vmap tables - core regs, then fp regs - into vmap_table
1023 std::vector<uint16_t> vmap_table;
1024 // Core regs may have been inserted out of order - sort first
1025 std::sort(core_vmap_table_.begin(), core_vmap_table_.end());
1026 for (size_t i = 0 ; i < core_vmap_table_.size(); i++) {
1027 // Copy, stripping out the phys register sort key
1028 vmap_table.push_back(~(-1 << VREG_NUM_WIDTH) & core_vmap_table_[i]);
1029 }
1030 // If we have a frame, push a marker to take place of lr
1031 if (frame_size_ > 0) {
1032 vmap_table.push_back(INVALID_VREG);
1033 } else {
1034 DCHECK_EQ(__builtin_popcount(core_spill_mask_), 0);
1035 DCHECK_EQ(__builtin_popcount(fp_spill_mask_), 0);
1036 }
1037 // Combine vmap tables - core regs, then fp regs. fp regs already sorted
1038 for (uint32_t i = 0; i < fp_vmap_table_.size(); i++) {
1039 vmap_table.push_back(fp_vmap_table_[i]);
1040 }
1041 CompiledMethod* result =
1042 new CompiledMethod(cu_->instruction_set, code_buffer_,
1043 frame_size_, core_spill_mask_, fp_spill_mask_,
1044 combined_mapping_table_, vmap_table, native_gc_map_);
1045 return result;
1046}
1047
1048int Mir2Lir::ComputeFrameSize() {
1049 /* Figure out the frame size */
1050 static const uint32_t kAlignMask = kStackAlignment - 1;
1051 uint32_t size = (num_core_spills_ + num_fp_spills_ +
1052 1 /* filler word */ + cu_->num_regs + cu_->num_outs +
1053 cu_->num_compiler_temps + 1 /* cur_method* */)
1054 * sizeof(uint32_t);
1055 /* Align and set */
1056 return (size + kAlignMask) & ~(kAlignMask);
1057}
1058
1059/*
1060 * Append an LIR instruction to the LIR list maintained by a compilation
1061 * unit
1062 */
1063void Mir2Lir::AppendLIR(LIR* lir)
1064{
1065 if (first_lir_insn_ == NULL) {
1066 DCHECK(last_lir_insn_ == NULL);
1067 last_lir_insn_ = first_lir_insn_ = lir;
1068 lir->prev = lir->next = NULL;
1069 } else {
1070 last_lir_insn_->next = lir;
1071 lir->prev = last_lir_insn_;
1072 lir->next = NULL;
1073 last_lir_insn_ = lir;
1074 }
1075}
1076
1077/*
1078 * Insert an LIR instruction before the current instruction, which cannot be the
1079 * first instruction.
1080 *
1081 * prev_lir <-> new_lir <-> current_lir
1082 */
1083void Mir2Lir::InsertLIRBefore(LIR* current_lir, LIR* new_lir)
1084{
1085 DCHECK(current_lir->prev != NULL);
1086 LIR *prev_lir = current_lir->prev;
1087
1088 prev_lir->next = new_lir;
1089 new_lir->prev = prev_lir;
1090 new_lir->next = current_lir;
1091 current_lir->prev = new_lir;
1092}
1093
1094/*
1095 * Insert an LIR instruction after the current instruction, which cannot be the
1096 * first instruction.
1097 *
1098 * current_lir -> new_lir -> old_next
1099 */
1100void Mir2Lir::InsertLIRAfter(LIR* current_lir, LIR* new_lir)
1101{
1102 new_lir->prev = current_lir;
1103 new_lir->next = current_lir->next;
1104 current_lir->next = new_lir;
1105 new_lir->next->prev = new_lir;
1106}
1107
1108
buzbeea3a82b22012-11-27 16:09:55 -08001109} // namespace art