blob: fbeceef4d367bd02f6bf42a731efb2bd434583cb [file] [log] [blame]
Ben Chenge9695e52009-06-16 16:11:47 -07001/*
2 * Copyright (C) 2009 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 "vm/compiler/CompilerInternals.h"
Bill Buzbee89efc3d2009-07-28 11:22:22 -070019#include "ArmLIR.h"
Ben Cheng5d90c202009-11-22 23:31:11 -080020#include "Codegen.h"
Ben Chenge9695e52009-06-16 16:11:47 -070021
Ben Chengd7d426a2009-09-22 11:23:36 -070022#define DEBUG_OPT(X)
23
Bill Buzbee7ea0f642009-08-10 17:06:51 -070024ArmLIR* dvmCompilerGenCopy(CompilationUnit *cUnit, int rDest, int rSrc);
25
26/* Is this a Dalvik register access? */
27static inline bool isDalvikLoad(ArmLIR *lir)
28{
Bill Buzbee1f748632010-03-02 16:14:41 -080029 return (lir->useMask != ENCODE_ALL) && (lir->useMask & ENCODE_DALVIK_REG);
30}
31
32/* Is this a load from the literal pool? */
33static inline bool isLiteralLoad(ArmLIR *lir)
34{
35 return (lir->useMask != ENCODE_ALL) && (lir->useMask & ENCODE_LITERAL);
Bill Buzbee7ea0f642009-08-10 17:06:51 -070036}
37
38static inline bool isDalvikStore(ArmLIR *lir)
39{
Bill Buzbee1f748632010-03-02 16:14:41 -080040 return (lir->defMask != ENCODE_ALL) && (lir->defMask & ENCODE_DALVIK_REG);
Bill Buzbee7ea0f642009-08-10 17:06:51 -070041}
42
Ben Chenga4aaf682009-09-30 22:53:44 -070043static inline bool isDalvikRegisterClobbered(ArmLIR *lir1, ArmLIR *lir2)
Bill Buzbee270c1d62009-08-13 16:58:07 -070044{
Ben Chengd7d426a2009-09-22 11:23:36 -070045 int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->aliasInfo);
46 int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->aliasInfo);
47 int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->aliasInfo);
48 int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->aliasInfo);
49
Ben Chenga4aaf682009-09-30 22:53:44 -070050 return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
Bill Buzbee270c1d62009-08-13 16:58:07 -070051}
Ben Chengd7d426a2009-09-22 11:23:36 -070052
53static void dumpDependentInsnPair(ArmLIR *thisLIR, ArmLIR *checkLIR,
54 const char *optimization)
55{
56 LOGD("************ %s ************", optimization);
57 dvmDumpLIRInsn((LIR *) thisLIR, 0);
58 dvmDumpLIRInsn((LIR *) checkLIR, 0);
59}
60
Ben Chenge9695e52009-06-16 16:11:47 -070061/*
62 * Perform a pass of top-down walk to
63 * 1) Eliminate redundant loads and stores
64 * 2) Sink stores to latest possible slot
65 */
66static void applyLoadStoreElimination(CompilationUnit *cUnit,
Bill Buzbee89efc3d2009-07-28 11:22:22 -070067 ArmLIR *headLIR,
68 ArmLIR *tailLIR)
Ben Chenge9695e52009-06-16 16:11:47 -070069{
Bill Buzbee89efc3d2009-07-28 11:22:22 -070070 ArmLIR *thisLIR;
Ben Chenge9695e52009-06-16 16:11:47 -070071
72 cUnit->optRound++;
73 for (thisLIR = headLIR;
74 thisLIR != tailLIR;
75 thisLIR = NEXT_LIR(thisLIR)) {
76 /* Skip newly added instructions */
77 if (thisLIR->age >= cUnit->optRound) {
78 continue;
79 }
Bill Buzbee7ea0f642009-08-10 17:06:51 -070080 if (isDalvikStore(thisLIR)) {
Ben Chengd7d426a2009-09-22 11:23:36 -070081 int dRegId = DECODE_ALIAS_INFO_REG(thisLIR->aliasInfo);
82 int dRegIdHi = dRegId + DECODE_ALIAS_INFO_WIDE(thisLIR->aliasInfo);
Ben Chenge9695e52009-06-16 16:11:47 -070083 int nativeRegId = thisLIR->operands[0];
Bill Buzbee89efc3d2009-07-28 11:22:22 -070084 ArmLIR *checkLIR;
Ben Chenge9695e52009-06-16 16:11:47 -070085 int sinkDistance = 0;
Ben Chengdcf3e5d2009-09-11 13:42:05 -070086 /*
87 * Add r15 (pc) to the mask to prevent this instruction
Ben Chengd7d426a2009-09-22 11:23:36 -070088 * from sinking past branch instructions. Unset the Dalvik register
89 * bit when checking with native resource constraints.
Ben Chengdcf3e5d2009-09-11 13:42:05 -070090 */
Ben Chengd7d426a2009-09-22 11:23:36 -070091 u8 stopMask = (ENCODE_REG_PC | thisLIR->useMask) &
92 ~ENCODE_DALVIK_REG;
Ben Chenge9695e52009-06-16 16:11:47 -070093
94 for (checkLIR = NEXT_LIR(thisLIR);
95 checkLIR != tailLIR;
96 checkLIR = NEXT_LIR(checkLIR)) {
97
98 /* Check if a Dalvik register load is redundant */
Bill Buzbee7ea0f642009-08-10 17:06:51 -070099 if (isDalvikLoad(checkLIR) &&
Ben Chengd7d426a2009-09-22 11:23:36 -0700100 (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
101 (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) {
Ben Chenge9695e52009-06-16 16:11:47 -0700102 /* Insert a move to replace the load */
103 if (checkLIR->operands[0] != nativeRegId) {
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700104 ArmLIR *moveLIR;
Ben Cheng5d90c202009-11-22 23:31:11 -0800105 moveLIR = dvmCompilerRegCopyNoInsert(
106 cUnit, checkLIR->operands[0], nativeRegId);
Ben Chenge9695e52009-06-16 16:11:47 -0700107 /*
Ben Cheng58ece732010-03-17 23:32:26 -0700108 * Insert the converted checkLIR instruction after the
109 * the original checkLIR since the optimization is
110 * scannng in the top-down order and the new instruction
111 * will need to be checked.
Ben Chenge9695e52009-06-16 16:11:47 -0700112 */
Ben Cheng58ece732010-03-17 23:32:26 -0700113 dvmCompilerInsertLIRAfter((LIR *) checkLIR,
114 (LIR *) moveLIR);
Ben Chenge9695e52009-06-16 16:11:47 -0700115 }
116 checkLIR->isNop = true;
117 continue;
118
Ben Chengd7d426a2009-09-22 11:23:36 -0700119 /*
120 * Found a true output dependency - nuke the previous store.
121 * The register type doesn't matter here.
122 */
Bill Buzbee7ea0f642009-08-10 17:06:51 -0700123 } else if (isDalvikStore(checkLIR) &&
Ben Chengd7d426a2009-09-22 11:23:36 -0700124 (checkLIR->aliasInfo == thisLIR->aliasInfo)) {
Ben Chenge9695e52009-06-16 16:11:47 -0700125 thisLIR->isNop = true;
126 break;
127 /* Find out the latest slot that the store can be sunk into */
128 } else {
Ben Chenge9695e52009-06-16 16:11:47 -0700129 /* Last instruction reached */
Ben Chengd7d426a2009-09-22 11:23:36 -0700130 bool stopHere = (NEXT_LIR(checkLIR) == tailLIR);
Bill Buzbeea4a7f072009-08-27 13:58:09 -0700131
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700132 /* Store data is clobbered */
Ben Chengd7d426a2009-09-22 11:23:36 -0700133 stopHere |= ((stopMask & checkLIR->defMask) != 0);
134
135 /* Store data partially clobbers the Dalvik register */
136 if (stopHere == false &&
137 ((checkLIR->useMask | checkLIR->defMask) &
138 ENCODE_DALVIK_REG)) {
Ben Chenga4aaf682009-09-30 22:53:44 -0700139 stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR);
Ben Chengd7d426a2009-09-22 11:23:36 -0700140 }
Ben Chenge9695e52009-06-16 16:11:47 -0700141
142 /* Found a new place to put the store - move it here */
143 if (stopHere == true) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700144 DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
145 "SINK STORE"));
Ben Chenge9695e52009-06-16 16:11:47 -0700146 /* The store can be sunk for at least one cycle */
147 if (sinkDistance != 0) {
Bill Buzbee89efc3d2009-07-28 11:22:22 -0700148 ArmLIR *newStoreLIR =
149 dvmCompilerNew(sizeof(ArmLIR), true);
Ben Chenge9695e52009-06-16 16:11:47 -0700150 *newStoreLIR = *thisLIR;
151 newStoreLIR->age = cUnit->optRound;
152 /*
153 * Insertion is guaranteed to succeed since checkLIR
154 * is never the first LIR on the list
155 */
156 dvmCompilerInsertLIRBefore((LIR *) checkLIR,
157 (LIR *) newStoreLIR);
158 thisLIR->isNop = true;
159 }
160 break;
161 }
162
163 /*
164 * Saw a real instruction that the store can be sunk after
165 */
166 if (!isPseudoOpCode(checkLIR->opCode)) {
167 sinkDistance++;
168 }
169 }
170 }
171 }
172 }
173}
174
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700175static void applyLoadHoisting(CompilationUnit *cUnit,
176 ArmLIR *headLIR,
177 ArmLIR *tailLIR)
178{
179 ArmLIR *thisLIR;
Bill Buzbee1f748632010-03-02 16:14:41 -0800180 /*
181 * Don't want to hoist in front of first load following a barrier (or
182 * first instruction of the block.
183 */
184 bool firstLoad = true;
185 int maxHoist = dvmCompilerTargetOptHint(kMaxHoistDistance);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700186
187 cUnit->optRound++;
188 for (thisLIR = headLIR;
189 thisLIR != tailLIR;
190 thisLIR = NEXT_LIR(thisLIR)) {
191 /* Skip newly added instructions */
192 if (thisLIR->age >= cUnit->optRound ||
193 thisLIR->isNop == true) {
194 continue;
195 }
Bill Buzbee1f748632010-03-02 16:14:41 -0800196
197 if (firstLoad && (EncodingMap[thisLIR->opCode].flags & IS_LOAD)) {
198 /*
199 * Ensure nothing will be hoisted in front of this load because
200 * it's result will likely be needed soon.
201 */
202 thisLIR->defMask |= ENCODE_MEM_USE;
203 firstLoad = false;
204 }
205
206 firstLoad |= (thisLIR->defMask == ENCODE_ALL);
207
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700208 if (isDalvikLoad(thisLIR)) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700209 int dRegId = DECODE_ALIAS_INFO_REG(thisLIR->aliasInfo);
210 int dRegIdHi = dRegId + DECODE_ALIAS_INFO_WIDE(thisLIR->aliasInfo);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700211 int nativeRegId = thisLIR->operands[0];
212 ArmLIR *checkLIR;
213 int hoistDistance = 0;
Ben Chengd7d426a2009-09-22 11:23:36 -0700214 u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask) &
Bill Buzbee1f748632010-03-02 16:14:41 -0800215 ~ENCODE_FRAME_REF;
216 u8 stopDefMask = thisLIR->defMask & ~ENCODE_FRAME_REF;
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700217
Ben Chengd7d426a2009-09-22 11:23:36 -0700218 /* First check if the load can be completely elinimated */
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700219 for (checkLIR = PREV_LIR(thisLIR);
220 checkLIR != headLIR;
221 checkLIR = PREV_LIR(checkLIR)) {
222
223 if (checkLIR->isNop) continue;
224
Ben Chengd7d426a2009-09-22 11:23:36 -0700225 /*
226 * Check if the Dalvik register is previously accessed
227 * with exactly the same type.
228 */
229 if ((isDalvikLoad(checkLIR) || isDalvikStore(checkLIR)) &&
230 (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
231 (checkLIR->operands[0] == nativeRegId)) {
232 /*
233 * If it is previously accessed but with a different type,
234 * the search will terminate later at the point checking
235 * for partially overlapping stores.
236 */
237 thisLIR->isNop = true;
238 break;
239 }
240
241 /*
242 * No earlier use/def can reach this load if:
243 * 1) Head instruction is reached
244 * 2) load target register is clobbered
245 * 3) A branch is seen (stopUseMask has the PC bit set).
246 */
247 if ((checkLIR == headLIR) ||
248 (stopUseMask | stopDefMask) & checkLIR->defMask) {
249 break;
250 }
251
252 /* Store data partially clobbers the Dalvik register */
253 if (isDalvikStore(checkLIR) &&
Ben Chenga4aaf682009-09-30 22:53:44 -0700254 isDalvikRegisterClobbered(thisLIR, checkLIR)) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700255 break;
256 }
257 }
258
259 /* The load has been eliminated */
260 if (thisLIR->isNop) continue;
261
262 /*
263 * The load cannot be eliminated. See if it can be hoisted to an
264 * earlier spot.
265 */
266 for (checkLIR = PREV_LIR(thisLIR);
267 /* empty by intention */;
268 checkLIR = PREV_LIR(checkLIR)) {
269
270 if (checkLIR->isNop) continue;
271
Bill Buzbee1f748632010-03-02 16:14:41 -0800272 /*
273 * Check if the "thisLIR" load is redundant
274 * NOTE: At one point, we also triggered if the checkLIR
275 * instruction was a load. However, that tended to insert
276 * a load/use dependency because the full scheduler is
277 * not yet complete. When it is, we chould also trigger
278 * on loads.
279 */
280 if (isDalvikStore(checkLIR) &&
Ben Chengd7d426a2009-09-22 11:23:36 -0700281 (checkLIR->aliasInfo == thisLIR->aliasInfo) &&
282 (REGTYPE(checkLIR->operands[0]) == REGTYPE(nativeRegId))) {
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700283 /* Insert a move to replace the load */
284 if (checkLIR->operands[0] != nativeRegId) {
285 ArmLIR *moveLIR;
Ben Cheng5d90c202009-11-22 23:31:11 -0800286 moveLIR = dvmCompilerRegCopyNoInsert(
287 cUnit, nativeRegId, checkLIR->operands[0]);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700288 /*
289 * Convert *thisLIR* load into a move
290 */
291 dvmCompilerInsertLIRAfter((LIR *) checkLIR,
292 (LIR *) moveLIR);
293 }
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700294 thisLIR->isNop = true;
295 break;
296
297 /* Find out if the load can be yanked past the checkLIR */
298 } else {
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700299 /* Last instruction reached */
Ben Chengd7d426a2009-09-22 11:23:36 -0700300 bool stopHere = (checkLIR == headLIR);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700301
302 /* Base address is clobbered by checkLIR */
Ben Chengd7d426a2009-09-22 11:23:36 -0700303 stopHere |= ((stopUseMask & checkLIR->defMask) != 0);
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700304
305 /* Load target clobbers use/def in checkLIR */
Ben Chengd7d426a2009-09-22 11:23:36 -0700306 stopHere |= ((stopDefMask &
307 (checkLIR->useMask | checkLIR->defMask)) != 0);
308
309 /* Store data partially clobbers the Dalvik register */
310 if (stopHere == false &&
311 (checkLIR->defMask & ENCODE_DALVIK_REG)) {
Ben Chenga4aaf682009-09-30 22:53:44 -0700312 stopHere = isDalvikRegisterClobbered(thisLIR, checkLIR);
Ben Chengd7d426a2009-09-22 11:23:36 -0700313 }
314
315 /*
316 * Stop at an earlier Dalvik load if the offset of checkLIR
317 * is not less than thisLIR
318 *
319 * Experiments show that doing
320 *
321 * ldr r1, [r5, #16]
322 * ldr r0, [r5, #20]
323 *
324 * is much faster than
325 *
326 * ldr r0, [r5, #20]
327 * ldr r1, [r5, #16]
328 */
329 if (isDalvikLoad(checkLIR)) {
330 int dRegId2 =
331 DECODE_ALIAS_INFO_REG(checkLIR->aliasInfo);
332 if (dRegId2 <= dRegId) {
333 stopHere = true;
334 }
335 }
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700336
Bill Buzbee1f748632010-03-02 16:14:41 -0800337 /* Don't go too far */
338 stopHere |= (hoistDistance >= maxHoist);
339
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700340 /* Found a new place to put the load - move it here */
341 if (stopHere == true) {
Ben Chengd7d426a2009-09-22 11:23:36 -0700342 DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
343 "HOIST LOAD"));
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700344 /* The store can be hoisted for at least one cycle */
345 if (hoistDistance != 0) {
346 ArmLIR *newLoadLIR =
347 dvmCompilerNew(sizeof(ArmLIR), true);
348 *newLoadLIR = *thisLIR;
349 newLoadLIR->age = cUnit->optRound;
350 /*
351 * Insertion is guaranteed to succeed since checkLIR
352 * is never the first LIR on the list
353 */
354 dvmCompilerInsertLIRAfter((LIR *) checkLIR,
355 (LIR *) newLoadLIR);
356 thisLIR->isNop = true;
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700357 }
358 break;
359 }
360
361 /*
Ben Chengd7d426a2009-09-22 11:23:36 -0700362 * Saw a real instruction that hosting the load is
363 * beneficial
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700364 */
365 if (!isPseudoOpCode(checkLIR->opCode)) {
366 hoistDistance++;
367 }
368 }
369 }
Bill Buzbee1f748632010-03-02 16:14:41 -0800370 } else if (isLiteralLoad(thisLIR)) {
371 int litVal = thisLIR->aliasInfo;
372 int nativeRegId = thisLIR->operands[0];
373 ArmLIR *checkLIR;
374 int hoistDistance = 0;
375 u8 stopUseMask = (ENCODE_REG_PC | thisLIR->useMask) &
376 ~ENCODE_LITPOOL_REF;
377 u8 stopDefMask = thisLIR->defMask & ~ENCODE_LITPOOL_REF;
378
379 /* First check if the load can be completely elinimated */
380 for (checkLIR = PREV_LIR(thisLIR);
381 checkLIR != headLIR;
382 checkLIR = PREV_LIR(checkLIR)) {
383
384 if (checkLIR->isNop) continue;
385
386 /* Reloading same literal into same tgt reg? Eliminate if so */
387 if (isLiteralLoad(checkLIR) &&
388 (checkLIR->aliasInfo == litVal) &&
389 (checkLIR->operands[0] == nativeRegId)) {
390 thisLIR->isNop = true;
391 break;
392 }
393
394 /*
395 * No earlier use/def can reach this load if:
396 * 1) Head instruction is reached
397 * 2) load target register is clobbered
398 * 3) A branch is seen (stopUseMask has the PC bit set).
399 */
400 if ((checkLIR == headLIR) ||
401 (stopUseMask | stopDefMask) & checkLIR->defMask) {
402 break;
403 }
404 }
405
406 /* The load has been eliminated */
407 if (thisLIR->isNop) continue;
408
409 /*
410 * The load cannot be eliminated. See if it can be hoisted to an
411 * earlier spot.
412 */
413 for (checkLIR = PREV_LIR(thisLIR);
414 /* empty by intention */;
415 checkLIR = PREV_LIR(checkLIR)) {
416
417 if (checkLIR->isNop) continue;
418
419 /*
420 * TUNING: once a full scheduler exists, check here
421 * for conversion of a redundant load into a copy similar
422 * to the way redundant loads are handled above.
423 */
424
425 /* Find out if the load can be yanked past the checkLIR */
426
427 /* Last instruction reached */
428 bool stopHere = (checkLIR == headLIR);
429
430 /* Base address is clobbered by checkLIR */
431 stopHere |= ((stopUseMask & checkLIR->defMask) != 0);
432
433 /* Load target clobbers use/def in checkLIR */
434 stopHere |= ((stopDefMask &
435 (checkLIR->useMask | checkLIR->defMask)) != 0);
436
437 /* Avoid re-ordering literal pool loads */
438 stopHere |= isLiteralLoad(checkLIR);
439
440 /* Don't go too far */
441 stopHere |= (hoistDistance >= maxHoist);
442
443 /* Found a new place to put the load - move it here */
444 if (stopHere == true) {
445 DEBUG_OPT(dumpDependentInsnPair(thisLIR, checkLIR,
446 "HOIST LOAD"));
447 /* The store can be hoisted for at least one cycle */
448 if (hoistDistance != 0) {
449 ArmLIR *newLoadLIR =
450 dvmCompilerNew(sizeof(ArmLIR), true);
451 *newLoadLIR = *thisLIR;
452 newLoadLIR->age = cUnit->optRound;
453 /*
454 * Insertion is guaranteed to succeed since checkLIR
455 * is never the first LIR on the list
456 */
457 dvmCompilerInsertLIRAfter((LIR *) checkLIR,
458 (LIR *) newLoadLIR);
459 thisLIR->isNop = true;
460 }
461 break;
462 }
463
464 /*
465 * Saw a real instruction that hosting the load is
466 * beneficial
467 */
468 if (!isPseudoOpCode(checkLIR->opCode)) {
469 hoistDistance++;
470 }
471 }
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700472 }
473 }
474}
475
Ben Chenge9695e52009-06-16 16:11:47 -0700476void dvmCompilerApplyLocalOptimizations(CompilationUnit *cUnit, LIR *headLIR,
477 LIR *tailLIR)
478{
Ben Chengdcf3e5d2009-09-11 13:42:05 -0700479 if (!(gDvmJit.disableOpt & (1 << kLoadStoreElimination))) {
480 applyLoadStoreElimination(cUnit, (ArmLIR *) headLIR,
481 (ArmLIR *) tailLIR);
482 }
483 if (!(gDvmJit.disableOpt & (1 << kLoadHoisting))) {
484 applyLoadHoisting(cUnit, (ArmLIR *) headLIR, (ArmLIR *) tailLIR);
485 }
Ben Chenge9695e52009-06-16 16:11:47 -0700486}