|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 // Copyright 2012 the V8 project authors. All rights reserved. |
|
3 // Redistribution and use in source and binary forms, with or without |
|
4 // modification, are permitted provided that the following conditions are |
|
5 // met: |
|
6 // |
|
7 // * Redistributions of source code must retain the above copyright |
|
8 // notice, this list of conditions and the following disclaimer. |
|
9 // * Redistributions in binary form must reproduce the above |
|
10 // copyright notice, this list of conditions and the following |
|
11 // disclaimer in the documentation and/or other materials provided |
|
12 // with the distribution. |
|
13 // * Neither the name of Google Inc. nor the names of its |
|
14 // contributors may be used to endorse or promote products derived |
|
15 // from this software without specific prior written permission. |
|
16 // |
|
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
28 |
|
29 #include "jit/arm/Simulator-arm.h" |
|
30 |
|
31 #include "mozilla/Casting.h" |
|
32 #include "mozilla/FloatingPoint.h" |
|
33 #include "mozilla/Likely.h" |
|
34 #include "mozilla/MathAlgorithms.h" |
|
35 |
|
36 #include "jit/arm/Assembler-arm.h" |
|
37 #include "jit/AsmJS.h" |
|
38 #include "vm/Runtime.h" |
|
39 |
|
40 extern "C" { |
|
41 |
|
42 int64_t |
|
43 __aeabi_idivmod(int x, int y) |
|
44 { |
|
45 uint32_t lo = uint32_t(x / y); |
|
46 uint32_t hi = uint32_t(x % y); |
|
47 return (int64_t(hi) << 32) | lo; |
|
48 } |
|
49 |
|
50 int64_t |
|
51 __aeabi_uidivmod(int x, int y) |
|
52 { |
|
53 uint32_t lo = uint32_t(x) / uint32_t(y); |
|
54 uint32_t hi = uint32_t(x) % uint32_t(y); |
|
55 return (int64_t(hi) << 32) | lo; |
|
56 } |
|
57 } |
|
58 |
|
59 namespace js { |
|
60 namespace jit { |
|
61 |
|
62 // Load/store multiple addressing mode. |
|
63 enum BlockAddrMode { |
|
64 // Alias modes for comparison when writeback does not matter. |
|
65 da_x = (0|0|0) << 21, // Decrement after. |
|
66 ia_x = (0|4|0) << 21, // Increment after. |
|
67 db_x = (8|0|0) << 21, // Decrement before. |
|
68 ib_x = (8|4|0) << 21, // Increment before. |
|
69 }; |
|
70 |
|
71 // Type of VFP register. Determines register encoding. |
|
72 enum VFPRegPrecision { |
|
73 kSinglePrecision = 0, |
|
74 kDoublePrecision = 1 |
|
75 }; |
|
76 |
|
77 enum NeonListType { |
|
78 nlt_1 = 0x7, |
|
79 nlt_2 = 0xA, |
|
80 nlt_3 = 0x6, |
|
81 nlt_4 = 0x2 |
|
82 }; |
|
83 |
|
84 // Supervisor Call (svc) specific support. |
|
85 |
|
86 // Special Software Interrupt codes when used in the presence of the ARM |
|
87 // simulator. |
|
88 // svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for |
|
89 // standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature. |
|
90 enum SoftwareInterruptCodes { |
|
91 kCallRtRedirected = 0x10, // Transition to C code. |
|
92 kBreakpoint= 0x20, // Breakpoint. |
|
93 kStopCode = 1 << 23 // Stop. |
|
94 }; |
|
95 |
|
96 const uint32_t kStopCodeMask = kStopCode - 1; |
|
97 const uint32_t kMaxStopCode = kStopCode - 1; |
|
98 |
|
99 // ----------------------------------------------------------------------------- |
|
100 // Instruction abstraction. |
|
101 |
|
102 // The class Instruction enables access to individual fields defined in the ARM |
|
103 // architecture instruction set encoding as described in figure A3-1. |
|
104 // Note that the Assembler uses typedef int32_t Instr. |
|
105 // |
|
106 // Example: Test whether the instruction at ptr does set the condition code |
|
107 // bits. |
|
108 // |
|
109 // bool InstructionSetsConditionCodes(byte* ptr) { |
|
110 // Instruction* instr = Instruction::At(ptr); |
|
111 // int type = instr->TypeValue(); |
|
112 // return ((type == 0) || (type == 1)) && instr->hasS(); |
|
113 // } |
|
114 // |
|
115 class SimInstruction { |
|
116 public: |
|
117 enum { |
|
118 kInstrSize = 4, |
|
119 kPCReadOffset = 8 |
|
120 }; |
|
121 |
|
122 // Get the raw instruction bits. |
|
123 inline Instr instructionBits() const { |
|
124 return *reinterpret_cast<const Instr*>(this); |
|
125 } |
|
126 |
|
127 // Set the raw instruction bits to value. |
|
128 inline void setInstructionBits(Instr value) { |
|
129 *reinterpret_cast<Instr*>(this) = value; |
|
130 } |
|
131 |
|
132 // Read one particular bit out of the instruction bits. |
|
133 inline int bit(int nr) const { |
|
134 return (instructionBits() >> nr) & 1; |
|
135 } |
|
136 |
|
137 // Read a bit field's value out of the instruction bits. |
|
138 inline int bits(int hi, int lo) const { |
|
139 return (instructionBits() >> lo) & ((2 << (hi - lo)) - 1); |
|
140 } |
|
141 |
|
142 // Read a bit field out of the instruction bits. |
|
143 inline int bitField(int hi, int lo) const { |
|
144 return instructionBits() & (((2 << (hi - lo)) - 1) << lo); |
|
145 } |
|
146 |
|
147 // Accessors for the different named fields used in the ARM encoding. |
|
148 // The naming of these accessor corresponds to figure A3-1. |
|
149 // |
|
150 // Two kind of accessors are declared: |
|
151 // - <Name>Field() will return the raw field, i.e. the field's bits at their |
|
152 // original place in the instruction encoding. |
|
153 // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as |
|
154 // 0xC0810002 conditionField(instr) will return 0xC0000000. |
|
155 // - <Name>Value() will return the field value, shifted back to bit 0. |
|
156 // e.g. if instr is the 'addgt r0, r1, r2' instruction, encoded as |
|
157 // 0xC0810002 conditionField(instr) will return 0xC. |
|
158 |
|
159 // Generally applicable fields |
|
160 inline Assembler::ARMCondition conditionField() const { |
|
161 return static_cast<Assembler::ARMCondition>(bitField(31, 28)); |
|
162 } |
|
163 inline int typeValue() const { return bits(27, 25); } |
|
164 inline int specialValue() const { return bits(27, 23); } |
|
165 |
|
166 inline int rnValue() const { return bits(19, 16); } |
|
167 inline int rdValue() const { return bits(15, 12); } |
|
168 |
|
169 inline int coprocessorValue() const { return bits(11, 8); } |
|
170 |
|
171 // Support for VFP. |
|
172 // Vn(19-16) | Vd(15-12) | Vm(3-0) |
|
173 inline int vnValue() const { return bits(19, 16); } |
|
174 inline int vmValue() const { return bits(3, 0); } |
|
175 inline int vdValue() const { return bits(15, 12); } |
|
176 inline int nValue() const { return bit(7); } |
|
177 inline int mValue() const { return bit(5); } |
|
178 inline int dValue() const { return bit(22); } |
|
179 inline int rtValue() const { return bits(15, 12); } |
|
180 inline int pValue() const { return bit(24); } |
|
181 inline int uValue() const { return bit(23); } |
|
182 inline int opc1Value() const { return (bit(23) << 2) | bits(21, 20); } |
|
183 inline int opc2Value() const { return bits(19, 16); } |
|
184 inline int opc3Value() const { return bits(7, 6); } |
|
185 inline int szValue() const { return bit(8); } |
|
186 inline int VLValue() const { return bit(20); } |
|
187 inline int VCValue() const { return bit(8); } |
|
188 inline int VAValue() const { return bits(23, 21); } |
|
189 inline int VBValue() const { return bits(6, 5); } |
|
190 inline int VFPNRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 16, 7); } |
|
191 inline int VFPMRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 0, 5); } |
|
192 inline int VFPDRegValue(VFPRegPrecision pre) { return VFPGlueRegValue(pre, 12, 22); } |
|
193 |
|
194 // Fields used in Data processing instructions |
|
195 inline int opcodeValue() const { return static_cast<ALUOp>(bits(24, 21)); } |
|
196 inline ALUOp opcodeField() const { return static_cast<ALUOp>(bitField(24, 21)); } |
|
197 inline int sValue() const { return bit(20); } |
|
198 |
|
199 // with register |
|
200 inline int rmValue() const { return bits(3, 0); } |
|
201 inline ShiftType shifttypeValue() const { return static_cast<ShiftType>(bits(6, 5)); } |
|
202 inline int rsValue() const { return bits(11, 8); } |
|
203 inline int shiftAmountValue() const { return bits(11, 7); } |
|
204 |
|
205 // with immediate |
|
206 inline int rotateValue() const { return bits(11, 8); } |
|
207 inline int immed8Value() const { return bits(7, 0); } |
|
208 inline int immed4Value() const { return bits(19, 16); } |
|
209 inline int immedMovwMovtValue() const { return immed4Value() << 12 | offset12Value(); } |
|
210 |
|
211 // Fields used in Load/Store instructions |
|
212 inline int PUValue() const { return bits(24, 23); } |
|
213 inline int PUField() const { return bitField(24, 23); } |
|
214 inline int bValue() const { return bit(22); } |
|
215 inline int wValue() const { return bit(21); } |
|
216 inline int lValue() const { return bit(20); } |
|
217 |
|
218 // with register uses same fields as Data processing instructions above |
|
219 // with immediate |
|
220 inline int offset12Value() const { return bits(11, 0); } |
|
221 |
|
222 // multiple |
|
223 inline int rlistValue() const { return bits(15, 0); } |
|
224 |
|
225 // extra loads and stores |
|
226 inline int signValue() const { return bit(6); } |
|
227 inline int hValue() const { return bit(5); } |
|
228 inline int immedHValue() const { return bits(11, 8); } |
|
229 inline int immedLValue() const { return bits(3, 0); } |
|
230 |
|
231 // Fields used in Branch instructions |
|
232 inline int linkValue() const { return bit(24); } |
|
233 inline int sImmed24Value() const { return ((instructionBits() << 8) >> 8); } |
|
234 |
|
235 // Fields used in Software interrupt instructions |
|
236 inline SoftwareInterruptCodes svcValue() const { |
|
237 return static_cast<SoftwareInterruptCodes>(bits(23, 0)); |
|
238 } |
|
239 |
|
240 // Test for special encodings of type 0 instructions (extra loads and stores, |
|
241 // as well as multiplications). |
|
242 inline bool isSpecialType0() const { return (bit(7) == 1) && (bit(4) == 1); } |
|
243 |
|
244 // Test for miscellaneous instructions encodings of type 0 instructions. |
|
245 inline bool isMiscType0() const { |
|
246 return bit(24) == 1 && bit(23) == 0 && bit(20) == 0 && (bit(7) == 0); |
|
247 } |
|
248 |
|
249 // Test for a nop instruction, which falls under type 1. |
|
250 inline bool isNopType1() const { return bits(24, 0) == 0x0120F000; } |
|
251 |
|
252 // Test for a stop instruction. |
|
253 inline bool isStop() const { |
|
254 return typeValue() == 7 && bit(24) == 1 && svcValue() >= kStopCode; |
|
255 } |
|
256 |
|
257 // Special accessors that test for existence of a value. |
|
258 inline bool hasS() const { return sValue() == 1; } |
|
259 inline bool hasB() const { return bValue() == 1; } |
|
260 inline bool hasW() const { return wValue() == 1; } |
|
261 inline bool hasL() const { return lValue() == 1; } |
|
262 inline bool hasU() const { return uValue() == 1; } |
|
263 inline bool hasSign() const { return signValue() == 1; } |
|
264 inline bool hasH() const { return hValue() == 1; } |
|
265 inline bool hasLink() const { return linkValue() == 1; } |
|
266 |
|
267 // Decoding the double immediate in the vmov instruction. |
|
268 double doubleImmedVmov() const; |
|
269 // Decoding the float32 immediate in the vmov.f32 instruction. |
|
270 float float32ImmedVmov() const; |
|
271 |
|
272 private: |
|
273 // Join split register codes, depending on single or double precision. |
|
274 // four_bit is the position of the least-significant bit of the four |
|
275 // bit specifier. one_bit is the position of the additional single bit |
|
276 // specifier. |
|
277 inline int VFPGlueRegValue(VFPRegPrecision pre, int four_bit, int one_bit) { |
|
278 if (pre == kSinglePrecision) |
|
279 return (bits(four_bit + 3, four_bit) << 1) | bit(one_bit); |
|
280 return (bit(one_bit) << 4) | bits(four_bit + 3, four_bit); |
|
281 } |
|
282 |
|
283 SimInstruction() MOZ_DELETE; |
|
284 SimInstruction(const SimInstruction &other) MOZ_DELETE; |
|
285 void operator=(const SimInstruction &other) MOZ_DELETE; |
|
286 }; |
|
287 |
|
288 double |
|
289 SimInstruction::doubleImmedVmov() const |
|
290 { |
|
291 // Reconstruct a double from the immediate encoded in the vmov instruction. |
|
292 // |
|
293 // instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh] |
|
294 // double: [aBbbbbbb,bbcdefgh,00000000,00000000, |
|
295 // 00000000,00000000,00000000,00000000] |
|
296 // |
|
297 // where B = ~b. Only the high 16 bits are affected. |
|
298 uint64_t high16; |
|
299 high16 = (bits(17, 16) << 4) | bits(3, 0); // xxxxxxxx,xxcdefgh. |
|
300 high16 |= (0xff * bit(18)) << 6; // xxbbbbbb,bbxxxxxx. |
|
301 high16 |= (bit(18) ^ 1) << 14; // xBxxxxxx,xxxxxxxx. |
|
302 high16 |= bit(19) << 15; // axxxxxxx,xxxxxxxx. |
|
303 |
|
304 uint64_t imm = high16 << 48; |
|
305 return mozilla::BitwiseCast<double>(imm); |
|
306 } |
|
307 |
|
308 float |
|
309 SimInstruction::float32ImmedVmov() const |
|
310 { |
|
311 // Reconstruct a float32 from the immediate encoded in the vmov instruction. |
|
312 // |
|
313 // instruction: [xxxxxxxx,xxxxabcd,xxxxxxxx,xxxxefgh] |
|
314 // float32: [aBbbbbbc, defgh000, 00000000, 00000000] |
|
315 // |
|
316 // where B = ~b. Only the high 16 bits are affected. |
|
317 uint32_t imm; |
|
318 imm = (bits(17, 16) << 23) | (bits(3, 0) << 19); // xxxxxxxc,defgh000.0.0 |
|
319 imm |= (0x1f * bit(18)) << 25; // xxbbbbbx,xxxxxxxx.0.0 |
|
320 imm |= (bit(18) ^ 1) << 30; // xBxxxxxx,xxxxxxxx.0.0 |
|
321 imm |= bit(19) << 31; // axxxxxxx,xxxxxxxx.0.0 |
|
322 |
|
323 return mozilla::BitwiseCast<float>(imm); |
|
324 } |
|
325 |
|
326 class CachePage |
|
327 { |
|
328 public: |
|
329 static const int LINE_VALID = 0; |
|
330 static const int LINE_INVALID = 1; |
|
331 static const int kPageShift = 12; |
|
332 static const int kPageSize = 1 << kPageShift; |
|
333 static const int kPageMask = kPageSize - 1; |
|
334 static const int kLineShift = 2; // The cache line is only 4 bytes right now. |
|
335 static const int kLineLength = 1 << kLineShift; |
|
336 static const int kLineMask = kLineLength - 1; |
|
337 |
|
338 CachePage() { |
|
339 memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); |
|
340 } |
|
341 char *validityByte(int offset) { |
|
342 return &validity_map_[offset >> kLineShift]; |
|
343 } |
|
344 char *cachedData(int offset) { |
|
345 return &data_[offset]; |
|
346 } |
|
347 |
|
348 private: |
|
349 char data_[kPageSize]; // The cached data. |
|
350 static const int kValidityMapSize = kPageSize >> kLineShift; |
|
351 char validity_map_[kValidityMapSize]; // One byte per line. |
|
352 }; |
|
353 |
|
354 class Redirection; |
|
355 |
|
356 class SimulatorRuntime |
|
357 { |
|
358 friend class AutoLockSimulatorRuntime; |
|
359 |
|
360 Redirection *redirection_; |
|
361 |
|
362 // ICache checking. |
|
363 struct ICacheHasher { |
|
364 typedef void *Key; |
|
365 typedef void *Lookup; |
|
366 static HashNumber hash(const Lookup &l); |
|
367 static bool match(const Key &k, const Lookup &l); |
|
368 }; |
|
369 |
|
370 public: |
|
371 typedef HashMap<void *, CachePage *, ICacheHasher, SystemAllocPolicy> ICacheMap; |
|
372 |
|
373 protected: |
|
374 ICacheMap icache_; |
|
375 |
|
376 // Synchronize access between main thread and compilation/PJS threads. |
|
377 PRLock *lock_; |
|
378 mozilla::DebugOnly<PRThread *> lockOwner_; |
|
379 |
|
380 public: |
|
381 SimulatorRuntime() |
|
382 : redirection_(nullptr), |
|
383 lock_(nullptr), |
|
384 lockOwner_(nullptr) |
|
385 {} |
|
386 ~SimulatorRuntime(); |
|
387 bool init() { |
|
388 #ifdef JS_THREADSAFE |
|
389 lock_ = PR_NewLock(); |
|
390 if (!lock_) |
|
391 return false; |
|
392 #endif |
|
393 if (!icache_.init()) |
|
394 return false; |
|
395 return true; |
|
396 } |
|
397 ICacheMap &icache() { |
|
398 #ifdef JS_THREADSAFE |
|
399 MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); |
|
400 #endif |
|
401 return icache_; |
|
402 } |
|
403 Redirection *redirection() const { |
|
404 #ifdef JS_THREADSAFE |
|
405 MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); |
|
406 #endif |
|
407 return redirection_; |
|
408 } |
|
409 void setRedirection(js::jit::Redirection *redirection) { |
|
410 #ifdef JS_THREADSAFE |
|
411 MOZ_ASSERT(lockOwner_ == PR_GetCurrentThread()); |
|
412 #endif |
|
413 redirection_ = redirection; |
|
414 } |
|
415 }; |
|
416 |
|
417 class AutoLockSimulatorRuntime |
|
418 { |
|
419 protected: |
|
420 SimulatorRuntime *srt_; |
|
421 |
|
422 public: |
|
423 AutoLockSimulatorRuntime(SimulatorRuntime *srt) |
|
424 : srt_(srt) |
|
425 { |
|
426 #ifdef JS_THREADSAFE |
|
427 PR_Lock(srt_->lock_); |
|
428 MOZ_ASSERT(!srt_->lockOwner_); |
|
429 #ifdef DEBUG |
|
430 srt_->lockOwner_ = PR_GetCurrentThread(); |
|
431 #endif |
|
432 #endif |
|
433 } |
|
434 |
|
435 ~AutoLockSimulatorRuntime() { |
|
436 #ifdef JS_THREADSAFE |
|
437 MOZ_ASSERT(srt_->lockOwner_ == PR_GetCurrentThread()); |
|
438 srt_->lockOwner_ = nullptr; |
|
439 PR_Unlock(srt_->lock_); |
|
440 #endif |
|
441 } |
|
442 }; |
|
443 |
|
444 bool Simulator::ICacheCheckingEnabled = false; |
|
445 |
|
446 int64_t Simulator::StopSimAt = -1L; |
|
447 |
|
448 SimulatorRuntime * |
|
449 CreateSimulatorRuntime() |
|
450 { |
|
451 SimulatorRuntime *srt = js_new<SimulatorRuntime>(); |
|
452 if (!srt) |
|
453 return nullptr; |
|
454 |
|
455 if (!srt->init()) { |
|
456 js_delete(srt); |
|
457 return nullptr; |
|
458 } |
|
459 |
|
460 if (getenv("ARM_SIM_ICACHE_CHECKS")) |
|
461 Simulator::ICacheCheckingEnabled = true; |
|
462 |
|
463 char *stopAtStr = getenv("ARM_SIM_STOP_AT"); |
|
464 int64_t stopAt; |
|
465 if (stopAtStr && sscanf(stopAtStr, "%lld", &stopAt) == 1) { |
|
466 fprintf(stderr, "\nStopping simulation at icount %lld\n", stopAt); |
|
467 Simulator::StopSimAt = stopAt; |
|
468 } |
|
469 |
|
470 return srt; |
|
471 } |
|
472 |
|
473 void |
|
474 DestroySimulatorRuntime(SimulatorRuntime *srt) |
|
475 { |
|
476 js_delete(srt); |
|
477 } |
|
478 |
|
479 // The ArmDebugger class is used by the simulator while debugging simulated ARM |
|
480 // code. |
|
481 class ArmDebugger { |
|
482 public: |
|
483 explicit ArmDebugger(Simulator *sim) : sim_(sim) { } |
|
484 |
|
485 void stop(SimInstruction *instr); |
|
486 void debug(); |
|
487 |
|
488 private: |
|
489 static const Instr kBreakpointInstr = (Assembler::AL | (7 * (1 << 25)) | (1* (1 << 24)) | kBreakpoint); |
|
490 static const Instr kNopInstr = (Assembler::AL | (13 * (1 << 21))); |
|
491 |
|
492 Simulator* sim_; |
|
493 |
|
494 int32_t getRegisterValue(int regnum); |
|
495 double getRegisterPairDoubleValue(int regnum); |
|
496 double getVFPDoubleRegisterValue(int regnum); |
|
497 bool getValue(const char *desc, int32_t *value); |
|
498 bool getVFPDoubleValue(const char *desc, double *value); |
|
499 |
|
500 // Set or delete a breakpoint. Returns true if successful. |
|
501 bool setBreakpoint(SimInstruction *breakpc); |
|
502 bool deleteBreakpoint(SimInstruction *breakpc); |
|
503 |
|
504 // Undo and redo all breakpoints. This is needed to bracket disassembly and |
|
505 // execution to skip past breakpoints when run from the debugger. |
|
506 void undoBreakpoints(); |
|
507 void redoBreakpoints(); |
|
508 }; |
|
509 |
|
510 void |
|
511 ArmDebugger::stop(SimInstruction * instr) |
|
512 { |
|
513 // Get the stop code. |
|
514 uint32_t code = instr->svcValue() & kStopCodeMask; |
|
515 // Retrieve the encoded address, which comes just after this stop. |
|
516 char* msg = *reinterpret_cast<char**>(sim_->get_pc() |
|
517 + SimInstruction::kInstrSize); |
|
518 // Update this stop description. |
|
519 if (sim_->isWatchedStop(code) && !sim_->watched_stops_[code].desc) { |
|
520 sim_->watched_stops_[code].desc = msg; |
|
521 } |
|
522 // Print the stop message and code if it is not the default code. |
|
523 if (code != kMaxStopCode) { |
|
524 printf("Simulator hit stop %u: %s\n", code, msg); |
|
525 } else { |
|
526 printf("Simulator hit %s\n", msg); |
|
527 } |
|
528 sim_->set_pc(sim_->get_pc() + 2 * SimInstruction::kInstrSize); |
|
529 debug(); |
|
530 } |
|
531 |
|
532 int32_t |
|
533 ArmDebugger::getRegisterValue(int regnum) |
|
534 { |
|
535 if (regnum == Registers::pc) |
|
536 return sim_->get_pc(); |
|
537 return sim_->get_register(regnum); |
|
538 } |
|
539 |
|
540 double |
|
541 ArmDebugger::getRegisterPairDoubleValue(int regnum) |
|
542 { |
|
543 return sim_->get_double_from_register_pair(regnum); |
|
544 } |
|
545 |
|
546 double |
|
547 ArmDebugger::getVFPDoubleRegisterValue(int regnum) |
|
548 { |
|
549 return sim_->get_double_from_d_register(regnum); |
|
550 } |
|
551 |
|
552 bool |
|
553 ArmDebugger::getValue(const char *desc, int32_t *value) |
|
554 { |
|
555 Register reg = Register::FromName(desc); |
|
556 if (reg != InvalidReg) { |
|
557 *value = getRegisterValue(reg.code()); |
|
558 return true; |
|
559 } |
|
560 if (strncmp(desc, "0x", 2) == 0) |
|
561 return sscanf(desc + 2, "%x", reinterpret_cast<uint32_t*>(value)) == 1; |
|
562 return sscanf(desc, "%u", reinterpret_cast<uint32_t*>(value)) == 1; |
|
563 } |
|
564 |
|
565 bool |
|
566 ArmDebugger::getVFPDoubleValue(const char *desc, double *value) |
|
567 { |
|
568 FloatRegister reg = FloatRegister::FromName(desc); |
|
569 if (reg != InvalidFloatReg) { |
|
570 *value = sim_->get_double_from_d_register(reg.code()); |
|
571 return true; |
|
572 } |
|
573 return false; |
|
574 } |
|
575 |
|
576 bool |
|
577 ArmDebugger::setBreakpoint(SimInstruction *breakpc) |
|
578 { |
|
579 // Check if a breakpoint can be set. If not return without any side-effects. |
|
580 if (sim_->break_pc_) |
|
581 return false; |
|
582 |
|
583 // Set the breakpoint. |
|
584 sim_->break_pc_ = breakpc; |
|
585 sim_->break_instr_ = breakpc->instructionBits(); |
|
586 // Not setting the breakpoint instruction in the code itself. It will be set |
|
587 // when the debugger shell continues. |
|
588 return true; |
|
589 } |
|
590 |
|
591 bool |
|
592 ArmDebugger::deleteBreakpoint(SimInstruction *breakpc) |
|
593 { |
|
594 if (sim_->break_pc_ != nullptr) |
|
595 sim_->break_pc_->setInstructionBits(sim_->break_instr_); |
|
596 |
|
597 sim_->break_pc_ = nullptr; |
|
598 sim_->break_instr_ = 0; |
|
599 return true; |
|
600 } |
|
601 |
|
602 void |
|
603 ArmDebugger::undoBreakpoints() |
|
604 { |
|
605 if (sim_->break_pc_) |
|
606 sim_->break_pc_->setInstructionBits(sim_->break_instr_); |
|
607 } |
|
608 |
|
609 void |
|
610 ArmDebugger::redoBreakpoints() |
|
611 { |
|
612 if (sim_->break_pc_) |
|
613 sim_->break_pc_->setInstructionBits(kBreakpointInstr); |
|
614 } |
|
615 |
|
616 static char * |
|
617 ReadLine(const char *prompt) |
|
618 { |
|
619 char *result = nullptr; |
|
620 char line_buf[256]; |
|
621 int offset = 0; |
|
622 bool keep_going = true; |
|
623 fprintf(stdout, "%s", prompt); |
|
624 fflush(stdout); |
|
625 while (keep_going) { |
|
626 if (fgets(line_buf, sizeof(line_buf), stdin) == nullptr) { |
|
627 // fgets got an error. Just give up. |
|
628 if (result) |
|
629 js_delete(result); |
|
630 return nullptr; |
|
631 } |
|
632 int len = strlen(line_buf); |
|
633 if (len > 0 && line_buf[len - 1] == '\n') { |
|
634 // Since we read a new line we are done reading the line. This |
|
635 // will exit the loop after copying this buffer into the result. |
|
636 keep_going = false; |
|
637 } |
|
638 if (!result) { |
|
639 // Allocate the initial result and make room for the terminating '\0' |
|
640 result = (char *)js_malloc(len + 1); |
|
641 if (!result) |
|
642 return nullptr; |
|
643 } else { |
|
644 // Allocate a new result with enough room for the new addition. |
|
645 int new_len = offset + len + 1; |
|
646 char *new_result = (char *)js_malloc(new_len); |
|
647 if (!new_result) |
|
648 return nullptr; |
|
649 // Copy the existing input into the new array and set the new |
|
650 // array as the result. |
|
651 memcpy(new_result, result, offset * sizeof(char)); |
|
652 js_free(result); |
|
653 result = new_result; |
|
654 } |
|
655 // Copy the newly read line into the result. |
|
656 memcpy(result + offset, line_buf, len * sizeof(char)); |
|
657 offset += len; |
|
658 } |
|
659 |
|
660 MOZ_ASSERT(result); |
|
661 result[offset] = '\0'; |
|
662 return result; |
|
663 } |
|
664 |
|
665 static void |
|
666 DisassembleInstruction(uint32_t pc) |
|
667 { |
|
668 uint8_t *bytes = reinterpret_cast<uint8_t *>(pc); |
|
669 char hexbytes[256]; |
|
670 sprintf(hexbytes, "0x%x 0x%x 0x%x 0x%x", bytes[0], bytes[1], bytes[2], bytes[3]); |
|
671 char llvmcmd[1024]; |
|
672 sprintf(llvmcmd, "bash -c \"echo -n '%p'; echo '%s' | " |
|
673 "llvm-mc -disassemble -arch=arm -mcpu=cortex-a9 | " |
|
674 "grep -v pure_instructions | grep -v .text\"", |
|
675 reinterpret_cast<void*>(pc), hexbytes); |
|
676 system(llvmcmd); |
|
677 } |
|
678 |
|
679 void |
|
680 ArmDebugger::debug() |
|
681 { |
|
682 intptr_t last_pc = -1; |
|
683 bool done = false; |
|
684 |
|
685 #define COMMAND_SIZE 63 |
|
686 #define ARG_SIZE 255 |
|
687 |
|
688 #define STR(a) #a |
|
689 #define XSTR(a) STR(a) |
|
690 |
|
691 char cmd[COMMAND_SIZE + 1]; |
|
692 char arg1[ARG_SIZE + 1]; |
|
693 char arg2[ARG_SIZE + 1]; |
|
694 char *argv[3] = { cmd, arg1, arg2 }; |
|
695 |
|
696 // make sure to have a proper terminating character if reaching the limit |
|
697 cmd[COMMAND_SIZE] = 0; |
|
698 arg1[ARG_SIZE] = 0; |
|
699 arg2[ARG_SIZE] = 0; |
|
700 |
|
701 // Undo all set breakpoints while running in the debugger shell. This will |
|
702 // make them invisible to all commands. |
|
703 undoBreakpoints(); |
|
704 |
|
705 while (!done && !sim_->has_bad_pc()) { |
|
706 if (last_pc != sim_->get_pc()) { |
|
707 DisassembleInstruction(sim_->get_pc()); |
|
708 last_pc = sim_->get_pc(); |
|
709 } |
|
710 char *line = ReadLine("sim> "); |
|
711 if (line == nullptr) { |
|
712 break; |
|
713 } else { |
|
714 char *last_input = sim_->lastDebuggerInput(); |
|
715 if (strcmp(line, "\n") == 0 && last_input != nullptr) { |
|
716 line = last_input; |
|
717 } else { |
|
718 // Ownership is transferred to sim_; |
|
719 sim_->setLastDebuggerInput(line); |
|
720 } |
|
721 |
|
722 // Use sscanf to parse the individual parts of the command line. At the |
|
723 // moment no command expects more than two parameters. |
|
724 int argc = sscanf(line, |
|
725 "%" XSTR(COMMAND_SIZE) "s " |
|
726 "%" XSTR(ARG_SIZE) "s " |
|
727 "%" XSTR(ARG_SIZE) "s", |
|
728 cmd, arg1, arg2); |
|
729 if (argc < 0) { |
|
730 continue; |
|
731 } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { |
|
732 sim_->instructionDecode(reinterpret_cast<SimInstruction *>(sim_->get_pc())); |
|
733 sim_->icount_++; |
|
734 } else if ((strcmp(cmd, "skip") == 0)) { |
|
735 sim_->set_pc(sim_->get_pc() + 4); |
|
736 sim_->icount_++; |
|
737 } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { |
|
738 // Execute the one instruction we broke at with breakpoints disabled. |
|
739 sim_->instructionDecode(reinterpret_cast<SimInstruction *>(sim_->get_pc())); |
|
740 sim_->icount_++; |
|
741 // Leave the debugger shell. |
|
742 done = true; |
|
743 } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { |
|
744 if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) { |
|
745 int32_t value; |
|
746 double dvalue; |
|
747 if (strcmp(arg1, "all") == 0) { |
|
748 for (uint32_t i = 0; i < Registers::Total; i++) { |
|
749 value = getRegisterValue(i); |
|
750 printf("%3s: 0x%08x %10d", Registers::GetName(i), value, value); |
|
751 if ((argc == 3 && strcmp(arg2, "fp") == 0) && |
|
752 i < 8 && |
|
753 (i % 2) == 0) { |
|
754 dvalue = getRegisterPairDoubleValue(i); |
|
755 printf(" (%f)\n", dvalue); |
|
756 } else { |
|
757 printf("\n"); |
|
758 } |
|
759 } |
|
760 for (uint32_t i = 0; i < FloatRegisters::Total; i++) { |
|
761 dvalue = getVFPDoubleRegisterValue(i); |
|
762 uint64_t as_words = mozilla::BitwiseCast<uint64_t>(dvalue); |
|
763 printf("%3s: %f 0x%08x %08x\n", |
|
764 FloatRegister::FromCode(i).name(), |
|
765 dvalue, |
|
766 static_cast<uint32_t>(as_words >> 32), |
|
767 static_cast<uint32_t>(as_words & 0xffffffff)); |
|
768 } |
|
769 } else { |
|
770 if (getValue(arg1, &value)) { |
|
771 printf("%s: 0x%08x %d \n", arg1, value, value); |
|
772 } else if (getVFPDoubleValue(arg1, &dvalue)) { |
|
773 uint64_t as_words = mozilla::BitwiseCast<uint64_t>(dvalue); |
|
774 printf("%s: %f 0x%08x %08x\n", |
|
775 arg1, |
|
776 dvalue, |
|
777 static_cast<uint32_t>(as_words >> 32), |
|
778 static_cast<uint32_t>(as_words & 0xffffffff)); |
|
779 } else { |
|
780 printf("%s unrecognized\n", arg1); |
|
781 } |
|
782 } |
|
783 } else { |
|
784 printf("print <register>\n"); |
|
785 } |
|
786 } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { |
|
787 int32_t *cur = nullptr; |
|
788 int32_t *end = nullptr; |
|
789 int next_arg = 1; |
|
790 |
|
791 if (strcmp(cmd, "stack") == 0) { |
|
792 cur = reinterpret_cast<int32_t*>(sim_->get_register(Simulator::sp)); |
|
793 } else { // "mem" |
|
794 int32_t value; |
|
795 if (!getValue(arg1, &value)) { |
|
796 printf("%s unrecognized\n", arg1); |
|
797 continue; |
|
798 } |
|
799 cur = reinterpret_cast<int32_t*>(value); |
|
800 next_arg++; |
|
801 } |
|
802 |
|
803 int32_t words; |
|
804 if (argc == next_arg) { |
|
805 words = 10; |
|
806 } else { |
|
807 if (!getValue(argv[next_arg], &words)) { |
|
808 words = 10; |
|
809 } |
|
810 } |
|
811 end = cur + words; |
|
812 |
|
813 while (cur < end) { |
|
814 printf(" %p: 0x%08x %10d", cur, *cur, *cur); |
|
815 printf("\n"); |
|
816 cur++; |
|
817 } |
|
818 } else if (strcmp(cmd, "disasm") == 0 || strcmp(cmd, "di") == 0) { |
|
819 uint8_t *cur = nullptr; |
|
820 uint8_t *end = nullptr; |
|
821 if (argc == 1) { |
|
822 cur = reinterpret_cast<uint8_t *>(sim_->get_pc()); |
|
823 end = cur + (10 * SimInstruction::kInstrSize); |
|
824 } else if (argc == 2) { |
|
825 Register reg = Register::FromName(arg1); |
|
826 if (reg != InvalidReg || strncmp(arg1, "0x", 2) == 0) { |
|
827 // The argument is an address or a register name. |
|
828 int32_t value; |
|
829 if (getValue(arg1, &value)) { |
|
830 cur = reinterpret_cast<uint8_t *>(value); |
|
831 // Disassemble 10 instructions at <arg1>. |
|
832 end = cur + (10 * SimInstruction::kInstrSize); |
|
833 } |
|
834 } else { |
|
835 // The argument is the number of instructions. |
|
836 int32_t value; |
|
837 if (getValue(arg1, &value)) { |
|
838 cur = reinterpret_cast<uint8_t *>(sim_->get_pc()); |
|
839 // Disassemble <arg1> instructions. |
|
840 end = cur + (value * SimInstruction::kInstrSize); |
|
841 } |
|
842 } |
|
843 } else { |
|
844 int32_t value1; |
|
845 int32_t value2; |
|
846 if (getValue(arg1, &value1) && getValue(arg2, &value2)) { |
|
847 cur = reinterpret_cast<uint8_t *>(value1); |
|
848 end = cur + (value2 * SimInstruction::kInstrSize); |
|
849 } |
|
850 } |
|
851 while (cur < end) { |
|
852 DisassembleInstruction(uint32_t(cur)); |
|
853 cur += SimInstruction::kInstrSize; |
|
854 } |
|
855 } else if (strcmp(cmd, "gdb") == 0) { |
|
856 printf("relinquishing control to gdb\n"); |
|
857 asm("int $3"); |
|
858 printf("regaining control from gdb\n"); |
|
859 } else if (strcmp(cmd, "break") == 0) { |
|
860 if (argc == 2) { |
|
861 int32_t value; |
|
862 if (getValue(arg1, &value)) { |
|
863 if (!setBreakpoint(reinterpret_cast<SimInstruction *>(value))) |
|
864 printf("setting breakpoint failed\n"); |
|
865 } else { |
|
866 printf("%s unrecognized\n", arg1); |
|
867 } |
|
868 } else { |
|
869 printf("break <address>\n"); |
|
870 } |
|
871 } else if (strcmp(cmd, "del") == 0) { |
|
872 if (!deleteBreakpoint(nullptr)) { |
|
873 printf("deleting breakpoint failed\n"); |
|
874 } |
|
875 } else if (strcmp(cmd, "flags") == 0) { |
|
876 printf("N flag: %d; ", sim_->n_flag_); |
|
877 printf("Z flag: %d; ", sim_->z_flag_); |
|
878 printf("C flag: %d; ", sim_->c_flag_); |
|
879 printf("V flag: %d\n", sim_->v_flag_); |
|
880 printf("INVALID OP flag: %d; ", sim_->inv_op_vfp_flag_); |
|
881 printf("DIV BY ZERO flag: %d; ", sim_->div_zero_vfp_flag_); |
|
882 printf("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_); |
|
883 printf("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_); |
|
884 printf("INEXACT flag: %d;\n", sim_->inexact_vfp_flag_); |
|
885 } else if (strcmp(cmd, "stop") == 0) { |
|
886 int32_t value; |
|
887 intptr_t stop_pc = sim_->get_pc() - 2 * SimInstruction::kInstrSize; |
|
888 SimInstruction *stop_instr = reinterpret_cast<SimInstruction *>(stop_pc); |
|
889 SimInstruction *msg_address = |
|
890 reinterpret_cast<SimInstruction *>(stop_pc + SimInstruction::kInstrSize); |
|
891 if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { |
|
892 // Remove the current stop. |
|
893 if (sim_->isStopInstruction(stop_instr)) { |
|
894 stop_instr->setInstructionBits(kNopInstr); |
|
895 msg_address->setInstructionBits(kNopInstr); |
|
896 } else { |
|
897 printf("Not at debugger stop.\n"); |
|
898 } |
|
899 } else if (argc == 3) { |
|
900 // Print information about all/the specified breakpoint(s). |
|
901 if (strcmp(arg1, "info") == 0) { |
|
902 if (strcmp(arg2, "all") == 0) { |
|
903 printf("Stop information:\n"); |
|
904 for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) |
|
905 sim_->printStopInfo(i); |
|
906 } else if (getValue(arg2, &value)) { |
|
907 sim_->printStopInfo(value); |
|
908 } else { |
|
909 printf("Unrecognized argument.\n"); |
|
910 } |
|
911 } else if (strcmp(arg1, "enable") == 0) { |
|
912 // Enable all/the specified breakpoint(s). |
|
913 if (strcmp(arg2, "all") == 0) { |
|
914 for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) |
|
915 sim_->enableStop(i); |
|
916 } else if (getValue(arg2, &value)) { |
|
917 sim_->enableStop(value); |
|
918 } else { |
|
919 printf("Unrecognized argument.\n"); |
|
920 } |
|
921 } else if (strcmp(arg1, "disable") == 0) { |
|
922 // Disable all/the specified breakpoint(s). |
|
923 if (strcmp(arg2, "all") == 0) { |
|
924 for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) { |
|
925 sim_->disableStop(i); |
|
926 } |
|
927 } else if (getValue(arg2, &value)) { |
|
928 sim_->disableStop(value); |
|
929 } else { |
|
930 printf("Unrecognized argument.\n"); |
|
931 } |
|
932 } |
|
933 } else { |
|
934 printf("Wrong usage. Use help command for more information.\n"); |
|
935 } |
|
936 } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { |
|
937 printf("cont\n"); |
|
938 printf(" continue execution (alias 'c')\n"); |
|
939 printf("skip\n"); |
|
940 printf(" skip one instruction (set pc to next instruction)\n"); |
|
941 printf("stepi\n"); |
|
942 printf(" step one instruction (alias 'si')\n"); |
|
943 printf("print <register>\n"); |
|
944 printf(" print register content (alias 'p')\n"); |
|
945 printf(" use register name 'all' to print all registers\n"); |
|
946 printf(" add argument 'fp' to print register pair double values\n"); |
|
947 printf("flags\n"); |
|
948 printf(" print flags\n"); |
|
949 printf("stack [<words>]\n"); |
|
950 printf(" dump stack content, default dump 10 words)\n"); |
|
951 printf("mem <address> [<words>]\n"); |
|
952 printf(" dump memory content, default dump 10 words)\n"); |
|
953 printf("disasm [<instructions>]\n"); |
|
954 printf("disasm [<address/register>]\n"); |
|
955 printf("disasm [[<address/register>] <instructions>]\n"); |
|
956 printf(" disassemble code, default is 10 instructions\n"); |
|
957 printf(" from pc (alias 'di')\n"); |
|
958 printf("gdb\n"); |
|
959 printf(" enter gdb\n"); |
|
960 printf("break <address>\n"); |
|
961 printf(" set a break point on the address\n"); |
|
962 printf("del\n"); |
|
963 printf(" delete the breakpoint\n"); |
|
964 printf("stop feature:\n"); |
|
965 printf(" Description:\n"); |
|
966 printf(" Stops are debug instructions inserted by\n"); |
|
967 printf(" the Assembler::stop() function.\n"); |
|
968 printf(" When hitting a stop, the Simulator will\n"); |
|
969 printf(" stop and and give control to the ArmDebugger.\n"); |
|
970 printf(" The first %d stop codes are watched:\n", |
|
971 Simulator::kNumOfWatchedStops); |
|
972 printf(" - They can be enabled / disabled: the Simulator\n"); |
|
973 printf(" will / won't stop when hitting them.\n"); |
|
974 printf(" - The Simulator keeps track of how many times they \n"); |
|
975 printf(" are met. (See the info command.) Going over a\n"); |
|
976 printf(" disabled stop still increases its counter. \n"); |
|
977 printf(" Commands:\n"); |
|
978 printf(" stop info all/<code> : print infos about number <code>\n"); |
|
979 printf(" or all stop(s).\n"); |
|
980 printf(" stop enable/disable all/<code> : enables / disables\n"); |
|
981 printf(" all or number <code> stop(s)\n"); |
|
982 printf(" stop unstop\n"); |
|
983 printf(" ignore the stop instruction at the current location\n"); |
|
984 printf(" from now on\n"); |
|
985 } else { |
|
986 printf("Unknown command: %s\n", cmd); |
|
987 } |
|
988 } |
|
989 } |
|
990 |
|
991 // Add all the breakpoints back to stop execution and enter the debugger |
|
992 // shell when hit. |
|
993 redoBreakpoints(); |
|
994 |
|
995 #undef COMMAND_SIZE |
|
996 #undef ARG_SIZE |
|
997 |
|
998 #undef STR |
|
999 #undef XSTR |
|
1000 } |
|
1001 |
|
1002 static bool |
|
1003 AllOnOnePage(uintptr_t start, int size) |
|
1004 { |
|
1005 intptr_t start_page = (start & ~CachePage::kPageMask); |
|
1006 intptr_t end_page = ((start + size) & ~CachePage::kPageMask); |
|
1007 return start_page == end_page; |
|
1008 } |
|
1009 |
|
1010 static CachePage * |
|
1011 GetCachePage(SimulatorRuntime::ICacheMap &i_cache, void *page) |
|
1012 { |
|
1013 MOZ_ASSERT(Simulator::ICacheCheckingEnabled); |
|
1014 |
|
1015 SimulatorRuntime::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); |
|
1016 if (p) |
|
1017 return p->value(); |
|
1018 |
|
1019 CachePage *new_page = js_new<CachePage>(); |
|
1020 if (!i_cache.add(p, page, new_page)) |
|
1021 return nullptr; |
|
1022 return new_page; |
|
1023 } |
|
1024 |
|
1025 // Flush from start up to and not including start + size. |
|
1026 static void |
|
1027 FlushOnePage(SimulatorRuntime::ICacheMap &i_cache, intptr_t start, int size) |
|
1028 { |
|
1029 MOZ_ASSERT(size <= CachePage::kPageSize); |
|
1030 MOZ_ASSERT(AllOnOnePage(start, size - 1)); |
|
1031 MOZ_ASSERT((start & CachePage::kLineMask) == 0); |
|
1032 MOZ_ASSERT((size & CachePage::kLineMask) == 0); |
|
1033 |
|
1034 void *page = reinterpret_cast<void*>(start & (~CachePage::kPageMask)); |
|
1035 int offset = (start & CachePage::kPageMask); |
|
1036 CachePage *cache_page = GetCachePage(i_cache, page); |
|
1037 char *valid_bytemap = cache_page->validityByte(offset); |
|
1038 memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); |
|
1039 } |
|
1040 |
|
1041 static void |
|
1042 FlushICache(SimulatorRuntime::ICacheMap &i_cache, void *start_addr, size_t size) |
|
1043 { |
|
1044 intptr_t start = reinterpret_cast<intptr_t>(start_addr); |
|
1045 int intra_line = (start & CachePage::kLineMask); |
|
1046 start -= intra_line; |
|
1047 size += intra_line; |
|
1048 size = ((size - 1) | CachePage::kLineMask) + 1; |
|
1049 int offset = (start & CachePage::kPageMask); |
|
1050 while (!AllOnOnePage(start, size - 1)) { |
|
1051 int bytes_to_flush = CachePage::kPageSize - offset; |
|
1052 FlushOnePage(i_cache, start, bytes_to_flush); |
|
1053 start += bytes_to_flush; |
|
1054 size -= bytes_to_flush; |
|
1055 MOZ_ASSERT((start & CachePage::kPageMask) == 0); |
|
1056 offset = 0; |
|
1057 } |
|
1058 if (size != 0) |
|
1059 FlushOnePage(i_cache, start, size); |
|
1060 } |
|
1061 |
|
1062 static void |
|
1063 CheckICache(SimulatorRuntime::ICacheMap &i_cache, SimInstruction *instr) |
|
1064 { |
|
1065 intptr_t address = reinterpret_cast<intptr_t>(instr); |
|
1066 void *page = reinterpret_cast<void*>(address & (~CachePage::kPageMask)); |
|
1067 void *line = reinterpret_cast<void*>(address & (~CachePage::kLineMask)); |
|
1068 int offset = (address & CachePage::kPageMask); |
|
1069 CachePage* cache_page = GetCachePage(i_cache, page); |
|
1070 char *cache_valid_byte = cache_page->validityByte(offset); |
|
1071 bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); |
|
1072 char *cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask); |
|
1073 if (cache_hit) { |
|
1074 // Check that the data in memory matches the contents of the I-cache. |
|
1075 MOZ_ASSERT(memcmp(reinterpret_cast<void*>(instr), |
|
1076 cache_page->cachedData(offset), |
|
1077 SimInstruction::kInstrSize) == 0); |
|
1078 } else { |
|
1079 // Cache miss. Load memory into the cache. |
|
1080 memcpy(cached_line, line, CachePage::kLineLength); |
|
1081 *cache_valid_byte = CachePage::LINE_VALID; |
|
1082 } |
|
1083 } |
|
1084 |
|
1085 HashNumber |
|
1086 SimulatorRuntime::ICacheHasher::hash(const Lookup &l) |
|
1087 { |
|
1088 return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(l)) >> 2; |
|
1089 } |
|
1090 |
|
1091 bool |
|
1092 SimulatorRuntime::ICacheHasher::match(const Key &k, const Lookup &l) |
|
1093 { |
|
1094 MOZ_ASSERT((reinterpret_cast<intptr_t>(k) & CachePage::kPageMask) == 0); |
|
1095 MOZ_ASSERT((reinterpret_cast<intptr_t>(l) & CachePage::kPageMask) == 0); |
|
1096 return k == l; |
|
1097 } |
|
1098 |
|
1099 void |
|
1100 Simulator::setLastDebuggerInput(char *input) |
|
1101 { |
|
1102 js_free(lastDebuggerInput_); |
|
1103 lastDebuggerInput_ = input; |
|
1104 } |
|
1105 |
|
1106 void |
|
1107 Simulator::FlushICache(void *start_addr, size_t size) |
|
1108 { |
|
1109 IonSpewCont(IonSpew_CacheFlush, "[%p %zx]", start_addr, size); |
|
1110 if (!Simulator::ICacheCheckingEnabled) |
|
1111 return; |
|
1112 SimulatorRuntime *srt = Simulator::Current()->srt_; |
|
1113 AutoLockSimulatorRuntime alsr(srt); |
|
1114 js::jit::FlushICache(srt->icache(), start_addr, size); |
|
1115 } |
|
1116 |
|
1117 Simulator::~Simulator() |
|
1118 { |
|
1119 js_free(stack_); |
|
1120 } |
|
1121 |
|
1122 Simulator::Simulator(SimulatorRuntime *srt) |
|
1123 : srt_(srt) |
|
1124 { |
|
1125 // Set up simulator support first. Some of this information is needed to |
|
1126 // setup the architecture state. |
|
1127 |
|
1128 // Allocate 2MB for the stack. Note that we will only use 1MB, see also |
|
1129 // Simulator::stackLimit(). |
|
1130 static const size_t stackSize = 2 * 1024*1024; |
|
1131 stack_ = reinterpret_cast<char*>(js_malloc(stackSize)); |
|
1132 if (!stack_) { |
|
1133 MOZ_ReportAssertionFailure("[unhandlable oom] Simulator stack", __FILE__, __LINE__); |
|
1134 MOZ_CRASH(); |
|
1135 } |
|
1136 pc_modified_ = false; |
|
1137 icount_ = 0L; |
|
1138 resume_pc_ = 0; |
|
1139 break_pc_ = nullptr; |
|
1140 break_instr_ = 0; |
|
1141 |
|
1142 // Set up architecture state. |
|
1143 // All registers are initialized to zero to start with. |
|
1144 for (int i = 0; i < num_registers; i++) |
|
1145 registers_[i] = 0; |
|
1146 |
|
1147 n_flag_ = false; |
|
1148 z_flag_ = false; |
|
1149 c_flag_ = false; |
|
1150 v_flag_ = false; |
|
1151 |
|
1152 for (int i = 0; i < num_d_registers * 2; i++) |
|
1153 vfp_registers_[i] = 0; |
|
1154 |
|
1155 n_flag_FPSCR_ = false; |
|
1156 z_flag_FPSCR_ = false; |
|
1157 c_flag_FPSCR_ = false; |
|
1158 v_flag_FPSCR_ = false; |
|
1159 FPSCR_rounding_mode_ = SimRZ; |
|
1160 FPSCR_default_NaN_mode_ = true; |
|
1161 |
|
1162 inv_op_vfp_flag_ = false; |
|
1163 div_zero_vfp_flag_ = false; |
|
1164 overflow_vfp_flag_ = false; |
|
1165 underflow_vfp_flag_ = false; |
|
1166 inexact_vfp_flag_ = false; |
|
1167 |
|
1168 // The sp is initialized to point to the bottom (high address) of the |
|
1169 // allocated stack area. To be safe in potential stack underflows we leave |
|
1170 // some buffer below. |
|
1171 registers_[sp] = reinterpret_cast<int32_t>(stack_) + stackSize - 64; |
|
1172 |
|
1173 // The lr and pc are initialized to a known bad value that will cause an |
|
1174 // access violation if the simulator ever tries to execute it. |
|
1175 registers_[pc] = bad_lr; |
|
1176 registers_[lr] = bad_lr; |
|
1177 |
|
1178 lastDebuggerInput_ = nullptr; |
|
1179 } |
|
1180 |
|
1181 // When the generated code calls a VM function (masm.callWithABI) we need to |
|
1182 // call that function instead of trying to execute it with the simulator |
|
1183 // (because it's x86 code instead of arm code). We do that by redirecting the |
|
1184 // VM call to a svc (Supervisor Call) instruction that is handled by the |
|
1185 // simulator. We write the original destination of the jump just at a known |
|
1186 // offset from the svc instruction so the simulator knows what to call. |
|
1187 class Redirection |
|
1188 { |
|
1189 friend class SimulatorRuntime; |
|
1190 |
|
1191 Redirection(void *nativeFunction, ABIFunctionType type) |
|
1192 : nativeFunction_(nativeFunction), |
|
1193 swiInstruction_(Assembler::AL | (0xf * (1 << 24)) | kCallRtRedirected), |
|
1194 type_(type), |
|
1195 next_(nullptr) |
|
1196 { |
|
1197 Simulator *sim = Simulator::Current(); |
|
1198 SimulatorRuntime *srt = sim->srt_; |
|
1199 next_ = srt->redirection(); |
|
1200 if (Simulator::ICacheCheckingEnabled) |
|
1201 FlushICache(srt->icache(), addressOfSwiInstruction(), SimInstruction::kInstrSize); |
|
1202 srt->setRedirection(this); |
|
1203 } |
|
1204 |
|
1205 public: |
|
1206 void *addressOfSwiInstruction() { return &swiInstruction_; } |
|
1207 void *nativeFunction() const { return nativeFunction_; } |
|
1208 ABIFunctionType type() const { return type_; } |
|
1209 |
|
1210 static Redirection *Get(void *nativeFunction, ABIFunctionType type) { |
|
1211 Simulator *sim = Simulator::Current(); |
|
1212 AutoLockSimulatorRuntime alsr(sim->srt_); |
|
1213 |
|
1214 Redirection *current = sim->srt_->redirection(); |
|
1215 for (; current != nullptr; current = current->next_) { |
|
1216 if (current->nativeFunction_ == nativeFunction) { |
|
1217 MOZ_ASSERT(current->type() == type); |
|
1218 return current; |
|
1219 } |
|
1220 } |
|
1221 |
|
1222 Redirection *redir = (Redirection *)js_malloc(sizeof(Redirection)); |
|
1223 if (!redir) { |
|
1224 MOZ_ReportAssertionFailure("[unhandlable oom] Simulator redirection", |
|
1225 __FILE__, __LINE__); |
|
1226 MOZ_CRASH(); |
|
1227 } |
|
1228 new(redir) Redirection(nativeFunction, type); |
|
1229 return redir; |
|
1230 } |
|
1231 |
|
1232 static Redirection *FromSwiInstruction(SimInstruction *swiInstruction) { |
|
1233 uint8_t *addrOfSwi = reinterpret_cast<uint8_t*>(swiInstruction); |
|
1234 uint8_t *addrOfRedirection = addrOfSwi - offsetof(Redirection, swiInstruction_); |
|
1235 return reinterpret_cast<Redirection*>(addrOfRedirection); |
|
1236 } |
|
1237 |
|
1238 private: |
|
1239 void *nativeFunction_; |
|
1240 uint32_t swiInstruction_; |
|
1241 ABIFunctionType type_; |
|
1242 Redirection *next_; |
|
1243 }; |
|
1244 |
|
1245 /* static */ void * |
|
1246 Simulator::RedirectNativeFunction(void *nativeFunction, ABIFunctionType type) |
|
1247 { |
|
1248 Redirection *redirection = Redirection::Get(nativeFunction, type); |
|
1249 return redirection->addressOfSwiInstruction(); |
|
1250 } |
|
1251 |
|
1252 SimulatorRuntime::~SimulatorRuntime() |
|
1253 { |
|
1254 Redirection *r = redirection_; |
|
1255 while (r) { |
|
1256 Redirection *next = r->next_; |
|
1257 js_delete(r); |
|
1258 r = next; |
|
1259 } |
|
1260 #ifdef JS_THREADSAFE |
|
1261 if (lock_) |
|
1262 PR_DestroyLock(lock_); |
|
1263 #endif |
|
1264 } |
|
1265 |
|
1266 // Sets the register in the architecture state. It will also deal with updating |
|
1267 // Simulator internal state for special registers such as PC. |
|
1268 void |
|
1269 Simulator::set_register(int reg, int32_t value) |
|
1270 { |
|
1271 MOZ_ASSERT(reg >= 0 && reg < num_registers); |
|
1272 if (reg == pc) |
|
1273 pc_modified_ = true; |
|
1274 registers_[reg] = value; |
|
1275 } |
|
1276 |
|
1277 // Get the register from the architecture state. This function does handle |
|
1278 // the special case of accessing the PC register. |
|
1279 int32_t |
|
1280 Simulator::get_register(int reg) const |
|
1281 { |
|
1282 MOZ_ASSERT(reg >= 0 && reg < num_registers); |
|
1283 // Work around GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 |
|
1284 if (reg >= num_registers) return 0; |
|
1285 return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0); |
|
1286 } |
|
1287 |
|
1288 double |
|
1289 Simulator::get_double_from_register_pair(int reg) |
|
1290 { |
|
1291 MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0); |
|
1292 |
|
1293 // Read the bits from the unsigned integer register_[] array |
|
1294 // into the double precision floating point value and return it. |
|
1295 double dm_val = 0.0; |
|
1296 char buffer[2 * sizeof(vfp_registers_[0])]; |
|
1297 memcpy(buffer, ®isters_[reg], 2 * sizeof(registers_[0])); |
|
1298 memcpy(&dm_val, buffer, 2 * sizeof(registers_[0])); |
|
1299 return dm_val; |
|
1300 } |
|
1301 |
|
1302 void |
|
1303 Simulator::set_register_pair_from_double(int reg, double *value) |
|
1304 { |
|
1305 MOZ_ASSERT(reg >= 0 && reg < num_registers && (reg % 2) == 0); |
|
1306 memcpy(registers_ + reg, value, sizeof(*value)); |
|
1307 } |
|
1308 |
|
1309 void |
|
1310 Simulator::set_dw_register(int dreg, const int *dbl) |
|
1311 { |
|
1312 MOZ_ASSERT(dreg >= 0 && dreg < num_d_registers); |
|
1313 registers_[dreg] = dbl[0]; |
|
1314 registers_[dreg + 1] = dbl[1]; |
|
1315 } |
|
1316 |
|
1317 void |
|
1318 Simulator::get_d_register(int dreg, uint64_t *value) |
|
1319 { |
|
1320 MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
|
1321 memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value)); |
|
1322 } |
|
1323 |
|
1324 void |
|
1325 Simulator::set_d_register(int dreg, const uint64_t *value) |
|
1326 { |
|
1327 MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
|
1328 memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value)); |
|
1329 } |
|
1330 |
|
1331 void |
|
1332 Simulator::get_d_register(int dreg, uint32_t *value) |
|
1333 { |
|
1334 MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
|
1335 memcpy(value, vfp_registers_ + dreg * 2, sizeof(*value) * 2); |
|
1336 } |
|
1337 |
|
1338 void |
|
1339 Simulator::set_d_register(int dreg, const uint32_t *value) |
|
1340 { |
|
1341 MOZ_ASSERT(dreg >= 0 && dreg < int(FloatRegisters::Total)); |
|
1342 memcpy(vfp_registers_ + dreg * 2, value, sizeof(*value) * 2); |
|
1343 } |
|
1344 |
|
1345 void |
|
1346 Simulator::get_q_register(int qreg, uint64_t *value) |
|
1347 { |
|
1348 MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); |
|
1349 memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 2); |
|
1350 } |
|
1351 |
|
1352 void |
|
1353 Simulator::set_q_register(int qreg, const uint64_t *value) |
|
1354 { |
|
1355 MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); |
|
1356 memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 2); |
|
1357 } |
|
1358 |
|
1359 void |
|
1360 Simulator::get_q_register(int qreg, uint32_t *value) |
|
1361 { |
|
1362 MOZ_ASSERT(qreg >= 0 && qreg < num_q_registers); |
|
1363 memcpy(value, vfp_registers_ + qreg * 4, sizeof(*value) * 4); |
|
1364 } |
|
1365 |
|
1366 void |
|
1367 Simulator::set_q_register(int qreg, const uint32_t *value) |
|
1368 { |
|
1369 MOZ_ASSERT((qreg >= 0) && (qreg < num_q_registers)); |
|
1370 memcpy(vfp_registers_ + qreg * 4, value, sizeof(*value) * 4); |
|
1371 } |
|
1372 |
|
1373 void |
|
1374 Simulator::set_pc(int32_t value) |
|
1375 { |
|
1376 pc_modified_ = true; |
|
1377 registers_[pc] = value; |
|
1378 } |
|
1379 |
|
1380 bool |
|
1381 Simulator::has_bad_pc() const |
|
1382 { |
|
1383 return registers_[pc] == bad_lr || registers_[pc] == end_sim_pc; |
|
1384 } |
|
1385 |
|
1386 // Raw access to the PC register without the special adjustment when reading. |
|
1387 int32_t |
|
1388 Simulator::get_pc() const |
|
1389 { |
|
1390 return registers_[pc]; |
|
1391 } |
|
1392 |
|
1393 void |
|
1394 Simulator::set_s_register(int sreg, unsigned int value) |
|
1395 { |
|
1396 MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers); |
|
1397 vfp_registers_[sreg] = value; |
|
1398 } |
|
1399 |
|
1400 unsigned |
|
1401 Simulator::get_s_register(int sreg) const |
|
1402 { |
|
1403 MOZ_ASSERT(sreg >= 0 && sreg < num_s_registers); |
|
1404 return vfp_registers_[sreg]; |
|
1405 } |
|
1406 |
|
1407 template<class InputType, int register_size> |
|
1408 void |
|
1409 Simulator::setVFPRegister(int reg_index, const InputType &value) |
|
1410 { |
|
1411 MOZ_ASSERT(reg_index >= 0); |
|
1412 MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers); |
|
1413 MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::Total)); |
|
1414 |
|
1415 char buffer[register_size * sizeof(vfp_registers_[0])]; |
|
1416 memcpy(buffer, &value, register_size * sizeof(vfp_registers_[0])); |
|
1417 memcpy(&vfp_registers_[reg_index * register_size], buffer, |
|
1418 register_size * sizeof(vfp_registers_[0])); |
|
1419 } |
|
1420 |
|
1421 template<class ReturnType, int register_size> |
|
1422 ReturnType Simulator::getFromVFPRegister(int reg_index) |
|
1423 { |
|
1424 MOZ_ASSERT(reg_index >= 0); |
|
1425 MOZ_ASSERT_IF(register_size == 1, reg_index < num_s_registers); |
|
1426 MOZ_ASSERT_IF(register_size == 2, reg_index < int(FloatRegisters::Total)); |
|
1427 |
|
1428 ReturnType value = 0; |
|
1429 char buffer[register_size * sizeof(vfp_registers_[0])]; |
|
1430 memcpy(buffer, &vfp_registers_[register_size * reg_index], |
|
1431 register_size * sizeof(vfp_registers_[0])); |
|
1432 memcpy(&value, buffer, register_size * sizeof(vfp_registers_[0])); |
|
1433 return value; |
|
1434 } |
|
1435 |
|
1436 void |
|
1437 Simulator::getFpArgs(double *x, double *y, int32_t *z) |
|
1438 { |
|
1439 if (useHardFpABI()) { |
|
1440 *x = get_double_from_d_register(0); |
|
1441 *y = get_double_from_d_register(1); |
|
1442 *z = get_register(0); |
|
1443 } else { |
|
1444 *x = get_double_from_register_pair(0); |
|
1445 *y = get_double_from_register_pair(2); |
|
1446 *z = get_register(2); |
|
1447 } |
|
1448 } |
|
1449 |
|
1450 void |
|
1451 Simulator::setCallResultDouble(double result) |
|
1452 { |
|
1453 // The return value is either in r0/r1 or d0. |
|
1454 if (useHardFpABI()) { |
|
1455 char buffer[2 * sizeof(vfp_registers_[0])]; |
|
1456 memcpy(buffer, &result, sizeof(buffer)); |
|
1457 // Copy result to d0. |
|
1458 memcpy(vfp_registers_, buffer, sizeof(buffer)); |
|
1459 } else { |
|
1460 char buffer[2 * sizeof(registers_[0])]; |
|
1461 memcpy(buffer, &result, sizeof(buffer)); |
|
1462 // Copy result to r0 and r1. |
|
1463 memcpy(registers_, buffer, sizeof(buffer)); |
|
1464 } |
|
1465 } |
|
1466 |
|
1467 void |
|
1468 Simulator::setCallResultFloat(float result) |
|
1469 { |
|
1470 if (useHardFpABI()) { |
|
1471 char buffer[sizeof(registers_[0])]; |
|
1472 memcpy(buffer, &result, sizeof(buffer)); |
|
1473 // Copy result to s0. |
|
1474 memcpy(vfp_registers_, buffer, sizeof(buffer)); |
|
1475 } else { |
|
1476 char buffer[sizeof(registers_[0])]; |
|
1477 memcpy(buffer, &result, sizeof(buffer)); |
|
1478 // Copy result to r0. |
|
1479 memcpy(registers_, buffer, sizeof(buffer)); |
|
1480 } |
|
1481 } |
|
1482 |
|
1483 void |
|
1484 Simulator::setCallResult(int64_t res) |
|
1485 { |
|
1486 set_register(r0, static_cast<int32_t>(res)); |
|
1487 set_register(r1, static_cast<int32_t>(res >> 32)); |
|
1488 } |
|
1489 |
|
1490 int |
|
1491 Simulator::readW(int32_t addr, SimInstruction *instr) |
|
1492 { |
|
1493 // YARR emits unaligned loads, so we don't check for them here like the |
|
1494 // other methods below. |
|
1495 intptr_t *ptr = reinterpret_cast<intptr_t*>(addr); |
|
1496 return *ptr; |
|
1497 } |
|
1498 |
|
1499 void |
|
1500 Simulator::writeW(int32_t addr, int value, SimInstruction *instr) |
|
1501 { |
|
1502 if ((addr & 3) == 0) { |
|
1503 intptr_t *ptr = reinterpret_cast<intptr_t*>(addr); |
|
1504 *ptr = value; |
|
1505 } else { |
|
1506 printf("Unaligned write at 0x%08x, pc=%p\n", addr, instr); |
|
1507 MOZ_CRASH(); |
|
1508 } |
|
1509 } |
|
1510 |
|
1511 uint16_t |
|
1512 Simulator::readHU(int32_t addr, SimInstruction *instr) |
|
1513 { |
|
1514 if ((addr & 1) == 0) { |
|
1515 uint16_t *ptr = reinterpret_cast<uint16_t*>(addr); |
|
1516 return *ptr; |
|
1517 } |
|
1518 printf("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr); |
|
1519 MOZ_CRASH(); |
|
1520 return 0; |
|
1521 } |
|
1522 |
|
1523 int16_t |
|
1524 Simulator::readH(int32_t addr, SimInstruction *instr) |
|
1525 { |
|
1526 if ((addr & 1) == 0) { |
|
1527 int16_t *ptr = reinterpret_cast<int16_t*>(addr); |
|
1528 return *ptr; |
|
1529 } |
|
1530 printf("Unaligned signed halfword read at 0x%08x\n", addr); |
|
1531 MOZ_CRASH(); |
|
1532 return 0; |
|
1533 } |
|
1534 |
|
1535 void |
|
1536 Simulator::writeH(int32_t addr, uint16_t value, SimInstruction *instr) |
|
1537 { |
|
1538 if ((addr & 1) == 0) { |
|
1539 uint16_t *ptr = reinterpret_cast<uint16_t*>(addr); |
|
1540 *ptr = value; |
|
1541 } else { |
|
1542 printf("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr); |
|
1543 MOZ_CRASH(); |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 void |
|
1548 Simulator::writeH(int32_t addr, int16_t value, SimInstruction *instr) |
|
1549 { |
|
1550 if ((addr & 1) == 0) { |
|
1551 int16_t *ptr = reinterpret_cast<int16_t*>(addr); |
|
1552 *ptr = value; |
|
1553 } else { |
|
1554 printf("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr); |
|
1555 MOZ_CRASH(); |
|
1556 } |
|
1557 } |
|
1558 |
|
1559 uint8_t |
|
1560 Simulator::readBU(int32_t addr) |
|
1561 { |
|
1562 uint8_t *ptr = reinterpret_cast<uint8_t*>(addr); |
|
1563 return *ptr; |
|
1564 } |
|
1565 |
|
1566 int8_t |
|
1567 Simulator::readB(int32_t addr) |
|
1568 { |
|
1569 int8_t *ptr = reinterpret_cast<int8_t*>(addr); |
|
1570 return *ptr; |
|
1571 } |
|
1572 |
|
1573 void |
|
1574 Simulator::writeB(int32_t addr, uint8_t value) |
|
1575 { |
|
1576 uint8_t *ptr = reinterpret_cast<uint8_t*>(addr); |
|
1577 *ptr = value; |
|
1578 } |
|
1579 |
|
1580 void |
|
1581 Simulator::writeB(int32_t addr, int8_t value) |
|
1582 { |
|
1583 int8_t *ptr = reinterpret_cast<int8_t*>(addr); |
|
1584 *ptr = value; |
|
1585 } |
|
1586 |
|
1587 int32_t * |
|
1588 Simulator::readDW(int32_t addr) |
|
1589 { |
|
1590 if ((addr & 3) == 0) { |
|
1591 int32_t *ptr = reinterpret_cast<int32_t*>(addr); |
|
1592 return ptr; |
|
1593 } |
|
1594 printf("Unaligned read at 0x%08x\n", addr); |
|
1595 MOZ_CRASH(); |
|
1596 return 0; |
|
1597 } |
|
1598 |
|
1599 void |
|
1600 Simulator::writeDW(int32_t addr, int32_t value1, int32_t value2) |
|
1601 { |
|
1602 if ((addr & 3) == 0) { |
|
1603 int32_t *ptr = reinterpret_cast<int32_t*>(addr); |
|
1604 *ptr++ = value1; |
|
1605 *ptr = value2; |
|
1606 } else { |
|
1607 printf("Unaligned write at 0x%08x\n", addr); |
|
1608 MOZ_CRASH(); |
|
1609 } |
|
1610 } |
|
1611 |
|
1612 uintptr_t |
|
1613 Simulator::stackLimit() const |
|
1614 { |
|
1615 // Leave a safety margin of 1MB to prevent overrunning the stack when |
|
1616 // pushing values (total stack size is 2MB). |
|
1617 return reinterpret_cast<uintptr_t>(stack_) + 1024 * 1024; |
|
1618 } |
|
1619 |
|
1620 bool |
|
1621 Simulator::overRecursed(uintptr_t newsp) const |
|
1622 { |
|
1623 if (newsp == 0) |
|
1624 newsp = get_register(sp); |
|
1625 return newsp <= stackLimit(); |
|
1626 } |
|
1627 |
|
1628 bool |
|
1629 Simulator::overRecursedWithExtra(uint32_t extra) const |
|
1630 { |
|
1631 uintptr_t newsp = get_register(sp) - extra; |
|
1632 return newsp <= stackLimit(); |
|
1633 } |
|
1634 |
|
1635 // Checks if the current instruction should be executed based on its |
|
1636 // condition bits. |
|
1637 bool |
|
1638 Simulator::conditionallyExecute(SimInstruction *instr) |
|
1639 { |
|
1640 switch (instr->conditionField()) { |
|
1641 case Assembler::EQ: return z_flag_; |
|
1642 case Assembler::NE: return !z_flag_; |
|
1643 case Assembler::CS: return c_flag_; |
|
1644 case Assembler::CC: return !c_flag_; |
|
1645 case Assembler::MI: return n_flag_; |
|
1646 case Assembler::PL: return !n_flag_; |
|
1647 case Assembler::VS: return v_flag_; |
|
1648 case Assembler::VC: return !v_flag_; |
|
1649 case Assembler::HI: return c_flag_ && !z_flag_; |
|
1650 case Assembler::LS: return !c_flag_ || z_flag_; |
|
1651 case Assembler::GE: return n_flag_ == v_flag_; |
|
1652 case Assembler::LT: return n_flag_ != v_flag_; |
|
1653 case Assembler::GT: return !z_flag_ && (n_flag_ == v_flag_); |
|
1654 case Assembler::LE: return z_flag_ || (n_flag_ != v_flag_); |
|
1655 case Assembler::AL: return true; |
|
1656 default: MOZ_ASSUME_UNREACHABLE(); |
|
1657 } |
|
1658 return false; |
|
1659 } |
|
1660 |
|
1661 // Calculate and set the Negative and Zero flags. |
|
1662 void |
|
1663 Simulator::setNZFlags(int32_t val) |
|
1664 { |
|
1665 n_flag_ = (val < 0); |
|
1666 z_flag_ = (val == 0); |
|
1667 } |
|
1668 |
|
1669 // Set the Carry flag. |
|
1670 void |
|
1671 Simulator::setCFlag(bool val) |
|
1672 { |
|
1673 c_flag_ = val; |
|
1674 } |
|
1675 |
|
1676 // Set the oVerflow flag. |
|
1677 void |
|
1678 Simulator::setVFlag(bool val) |
|
1679 { |
|
1680 v_flag_ = val; |
|
1681 } |
|
1682 |
|
1683 // Calculate C flag value for additions. |
|
1684 bool |
|
1685 Simulator::carryFrom(int32_t left, int32_t right, int32_t carry) |
|
1686 { |
|
1687 uint32_t uleft = static_cast<uint32_t>(left); |
|
1688 uint32_t uright = static_cast<uint32_t>(right); |
|
1689 uint32_t urest = 0xffffffffU - uleft; |
|
1690 return (uright > urest) || |
|
1691 (carry && (((uright + 1) > urest) || (uright > (urest - 1)))); |
|
1692 } |
|
1693 |
|
1694 // Calculate C flag value for subtractions. |
|
1695 bool |
|
1696 Simulator::borrowFrom(int32_t left, int32_t right) |
|
1697 { |
|
1698 uint32_t uleft = static_cast<uint32_t>(left); |
|
1699 uint32_t uright = static_cast<uint32_t>(right); |
|
1700 return (uright > uleft); |
|
1701 } |
|
1702 |
|
1703 // Calculate V flag value for additions and subtractions. |
|
1704 bool |
|
1705 Simulator::overflowFrom(int32_t alu_out, int32_t left, int32_t right, bool addition) |
|
1706 { |
|
1707 bool overflow; |
|
1708 if (addition) { |
|
1709 // operands have the same sign |
|
1710 overflow = ((left >= 0 && right >= 0) || (left < 0 && right < 0)) |
|
1711 // and operands and result have different sign |
|
1712 && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
|
1713 } else { |
|
1714 // operands have different signs |
|
1715 overflow = ((left < 0 && right >= 0) || (left >= 0 && right < 0)) |
|
1716 // and first operand and result have different signs |
|
1717 && ((left < 0 && alu_out >= 0) || (left >= 0 && alu_out < 0)); |
|
1718 } |
|
1719 return overflow; |
|
1720 } |
|
1721 |
|
1722 // Support for VFP comparisons. |
|
1723 void |
|
1724 Simulator::compute_FPSCR_Flags(double val1, double val2) |
|
1725 { |
|
1726 if (mozilla::IsNaN(val1) || mozilla::IsNaN(val2)) { |
|
1727 n_flag_FPSCR_ = false; |
|
1728 z_flag_FPSCR_ = false; |
|
1729 c_flag_FPSCR_ = true; |
|
1730 v_flag_FPSCR_ = true; |
|
1731 // All non-NaN cases. |
|
1732 } else if (val1 == val2) { |
|
1733 n_flag_FPSCR_ = false; |
|
1734 z_flag_FPSCR_ = true; |
|
1735 c_flag_FPSCR_ = true; |
|
1736 v_flag_FPSCR_ = false; |
|
1737 } else if (val1 < val2) { |
|
1738 n_flag_FPSCR_ = true; |
|
1739 z_flag_FPSCR_ = false; |
|
1740 c_flag_FPSCR_ = false; |
|
1741 v_flag_FPSCR_ = false; |
|
1742 } else { |
|
1743 // Case when (val1 > val2). |
|
1744 n_flag_FPSCR_ = false; |
|
1745 z_flag_FPSCR_ = false; |
|
1746 c_flag_FPSCR_ = true; |
|
1747 v_flag_FPSCR_ = false; |
|
1748 } |
|
1749 } |
|
1750 |
|
1751 void |
|
1752 Simulator::copy_FPSCR_to_APSR() |
|
1753 { |
|
1754 n_flag_ = n_flag_FPSCR_; |
|
1755 z_flag_ = z_flag_FPSCR_; |
|
1756 c_flag_ = c_flag_FPSCR_; |
|
1757 v_flag_ = v_flag_FPSCR_; |
|
1758 } |
|
1759 |
|
1760 // Addressing Mode 1 - Data-processing operands: |
|
1761 // Get the value based on the shifter_operand with register. |
|
1762 int32_t |
|
1763 Simulator::getShiftRm(SimInstruction *instr, bool *carry_out) |
|
1764 { |
|
1765 ShiftType shift = instr->shifttypeValue(); |
|
1766 int shift_amount = instr->shiftAmountValue(); |
|
1767 int32_t result = get_register(instr->rmValue()); |
|
1768 if (instr->bit(4) == 0) { |
|
1769 // By immediate. |
|
1770 if (shift == ROR && shift_amount == 0) { |
|
1771 MOZ_ASSUME_UNREACHABLE("NYI"); |
|
1772 return result; |
|
1773 } |
|
1774 if ((shift == LSR || shift == ASR) && shift_amount == 0) |
|
1775 shift_amount = 32; |
|
1776 switch (shift) { |
|
1777 case ASR: { |
|
1778 if (shift_amount == 0) { |
|
1779 if (result < 0) { |
|
1780 result = 0xffffffff; |
|
1781 *carry_out = true; |
|
1782 } else { |
|
1783 result = 0; |
|
1784 *carry_out = false; |
|
1785 } |
|
1786 } else { |
|
1787 result >>= (shift_amount - 1); |
|
1788 *carry_out = (result & 1) == 1; |
|
1789 result >>= 1; |
|
1790 } |
|
1791 break; |
|
1792 } |
|
1793 |
|
1794 case LSL: { |
|
1795 if (shift_amount == 0) { |
|
1796 *carry_out = c_flag_; |
|
1797 } else { |
|
1798 result <<= (shift_amount - 1); |
|
1799 *carry_out = (result < 0); |
|
1800 result <<= 1; |
|
1801 } |
|
1802 break; |
|
1803 } |
|
1804 |
|
1805 case LSR: { |
|
1806 if (shift_amount == 0) { |
|
1807 result = 0; |
|
1808 *carry_out = c_flag_; |
|
1809 } else { |
|
1810 uint32_t uresult = static_cast<uint32_t>(result); |
|
1811 uresult >>= (shift_amount - 1); |
|
1812 *carry_out = (uresult & 1) == 1; |
|
1813 uresult >>= 1; |
|
1814 result = static_cast<int32_t>(uresult); |
|
1815 } |
|
1816 break; |
|
1817 } |
|
1818 |
|
1819 case ROR: { |
|
1820 if (shift_amount == 0) { |
|
1821 *carry_out = c_flag_; |
|
1822 } else { |
|
1823 uint32_t left = static_cast<uint32_t>(result) >> shift_amount; |
|
1824 uint32_t right = static_cast<uint32_t>(result) << (32 - shift_amount); |
|
1825 result = right | left; |
|
1826 *carry_out = (static_cast<uint32_t>(result) >> 31) != 0; |
|
1827 } |
|
1828 break; |
|
1829 } |
|
1830 |
|
1831 default: |
|
1832 MOZ_ASSUME_UNREACHABLE(); |
|
1833 break; |
|
1834 } |
|
1835 } else { |
|
1836 // By register. |
|
1837 int rs = instr->rsValue(); |
|
1838 shift_amount = get_register(rs) &0xff; |
|
1839 switch (shift) { |
|
1840 case ASR: { |
|
1841 if (shift_amount == 0) { |
|
1842 *carry_out = c_flag_; |
|
1843 } else if (shift_amount < 32) { |
|
1844 result >>= (shift_amount - 1); |
|
1845 *carry_out = (result & 1) == 1; |
|
1846 result >>= 1; |
|
1847 } else { |
|
1848 MOZ_ASSERT(shift_amount >= 32); |
|
1849 if (result < 0) { |
|
1850 *carry_out = true; |
|
1851 result = 0xffffffff; |
|
1852 } else { |
|
1853 *carry_out = false; |
|
1854 result = 0; |
|
1855 } |
|
1856 } |
|
1857 break; |
|
1858 } |
|
1859 |
|
1860 case LSL: { |
|
1861 if (shift_amount == 0) { |
|
1862 *carry_out = c_flag_; |
|
1863 } else if (shift_amount < 32) { |
|
1864 result <<= (shift_amount - 1); |
|
1865 *carry_out = (result < 0); |
|
1866 result <<= 1; |
|
1867 } else if (shift_amount == 32) { |
|
1868 *carry_out = (result & 1) == 1; |
|
1869 result = 0; |
|
1870 } else { |
|
1871 MOZ_ASSERT(shift_amount > 32); |
|
1872 *carry_out = false; |
|
1873 result = 0; |
|
1874 } |
|
1875 break; |
|
1876 } |
|
1877 |
|
1878 case LSR: { |
|
1879 if (shift_amount == 0) { |
|
1880 *carry_out = c_flag_; |
|
1881 } else if (shift_amount < 32) { |
|
1882 uint32_t uresult = static_cast<uint32_t>(result); |
|
1883 uresult >>= (shift_amount - 1); |
|
1884 *carry_out = (uresult & 1) == 1; |
|
1885 uresult >>= 1; |
|
1886 result = static_cast<int32_t>(uresult); |
|
1887 } else if (shift_amount == 32) { |
|
1888 *carry_out = (result < 0); |
|
1889 result = 0; |
|
1890 } else { |
|
1891 *carry_out = false; |
|
1892 result = 0; |
|
1893 } |
|
1894 break; |
|
1895 } |
|
1896 |
|
1897 case ROR: { |
|
1898 if (shift_amount == 0) { |
|
1899 *carry_out = c_flag_; |
|
1900 } else { |
|
1901 uint32_t left = static_cast<uint32_t>(result) >> shift_amount; |
|
1902 uint32_t right = static_cast<uint32_t>(result) << (32 - shift_amount); |
|
1903 result = right | left; |
|
1904 *carry_out = (static_cast<uint32_t>(result) >> 31) != 0; |
|
1905 } |
|
1906 break; |
|
1907 } |
|
1908 |
|
1909 default: |
|
1910 MOZ_ASSUME_UNREACHABLE(); |
|
1911 break; |
|
1912 } |
|
1913 } |
|
1914 return result; |
|
1915 } |
|
1916 |
|
1917 // Addressing Mode 1 - Data-processing operands: |
|
1918 // Get the value based on the shifter_operand with immediate. |
|
1919 int32_t |
|
1920 Simulator::getImm(SimInstruction *instr, bool *carry_out) |
|
1921 { |
|
1922 int rotate = instr->rotateValue() * 2; |
|
1923 int immed8 = instr->immed8Value(); |
|
1924 int imm = (immed8 >> rotate) | (immed8 << (32 - rotate)); |
|
1925 *carry_out = (rotate == 0) ? c_flag_ : (imm < 0); |
|
1926 return imm; |
|
1927 } |
|
1928 |
|
1929 int32_t |
|
1930 Simulator::processPU(SimInstruction *instr, int num_regs, int reg_size, |
|
1931 intptr_t *start_address, intptr_t *end_address) |
|
1932 { |
|
1933 int rn = instr->rnValue(); |
|
1934 int32_t rn_val = get_register(rn); |
|
1935 switch (instr->PUField()) { |
|
1936 case da_x: |
|
1937 MOZ_CRASH(); |
|
1938 break; |
|
1939 case ia_x: |
|
1940 *start_address = rn_val; |
|
1941 *end_address = rn_val + (num_regs * reg_size) - reg_size; |
|
1942 rn_val = rn_val + (num_regs * reg_size); |
|
1943 break; |
|
1944 case db_x: |
|
1945 *start_address = rn_val - (num_regs * reg_size); |
|
1946 *end_address = rn_val - reg_size; |
|
1947 rn_val = *start_address; |
|
1948 break; |
|
1949 case ib_x: |
|
1950 *start_address = rn_val + reg_size; |
|
1951 *end_address = rn_val + (num_regs * reg_size); |
|
1952 rn_val = *end_address; |
|
1953 break; |
|
1954 default: |
|
1955 MOZ_ASSUME_UNREACHABLE(); |
|
1956 break; |
|
1957 } |
|
1958 return rn_val; |
|
1959 } |
|
1960 |
|
1961 // Addressing Mode 4 - Load and Store Multiple |
|
1962 void |
|
1963 Simulator::handleRList(SimInstruction *instr, bool load) |
|
1964 { |
|
1965 int rlist = instr->rlistValue(); |
|
1966 int num_regs = mozilla::CountPopulation32(rlist); |
|
1967 |
|
1968 intptr_t start_address = 0; |
|
1969 intptr_t end_address = 0; |
|
1970 int32_t rn_val = processPU(instr, num_regs, sizeof(void *), &start_address, &end_address); |
|
1971 intptr_t *address = reinterpret_cast<intptr_t*>(start_address); |
|
1972 |
|
1973 // Catch null pointers a little earlier. |
|
1974 MOZ_ASSERT(start_address > 8191 || start_address < 0); |
|
1975 |
|
1976 int reg = 0; |
|
1977 while (rlist != 0) { |
|
1978 if ((rlist & 1) != 0) { |
|
1979 if (load) { |
|
1980 set_register(reg, *address); |
|
1981 } else { |
|
1982 *address = get_register(reg); |
|
1983 } |
|
1984 address += 1; |
|
1985 } |
|
1986 reg++; |
|
1987 rlist >>= 1; |
|
1988 } |
|
1989 MOZ_ASSERT(end_address == ((intptr_t)address) - 4); |
|
1990 if (instr->hasW()) |
|
1991 set_register(instr->rnValue(), rn_val); |
|
1992 } |
|
1993 |
|
1994 // Addressing Mode 6 - Load and Store Multiple Coprocessor registers. |
|
1995 void |
|
1996 Simulator::handleVList(SimInstruction *instr) |
|
1997 { |
|
1998 VFPRegPrecision precision = (instr->szValue() == 0) ? kSinglePrecision : kDoublePrecision; |
|
1999 int operand_size = (precision == kSinglePrecision) ? 4 : 8; |
|
2000 bool load = (instr->VLValue() == 0x1); |
|
2001 |
|
2002 int vd; |
|
2003 int num_regs; |
|
2004 vd = instr->VFPDRegValue(precision); |
|
2005 if (precision == kSinglePrecision) |
|
2006 num_regs = instr->immed8Value(); |
|
2007 else |
|
2008 num_regs = instr->immed8Value() / 2; |
|
2009 |
|
2010 intptr_t start_address = 0; |
|
2011 intptr_t end_address = 0; |
|
2012 int32_t rn_val = processPU(instr, num_regs, operand_size, &start_address, &end_address); |
|
2013 |
|
2014 intptr_t *address = reinterpret_cast<intptr_t*>(start_address); |
|
2015 for (int reg = vd; reg < vd + num_regs; reg++) { |
|
2016 if (precision == kSinglePrecision) { |
|
2017 if (load) |
|
2018 set_s_register_from_sinteger(reg, readW(reinterpret_cast<int32_t>(address), instr)); |
|
2019 else |
|
2020 writeW(reinterpret_cast<int32_t>(address), get_sinteger_from_s_register(reg), instr); |
|
2021 address += 1; |
|
2022 } else { |
|
2023 if (load) { |
|
2024 int32_t data[] = { |
|
2025 readW(reinterpret_cast<int32_t>(address), instr), |
|
2026 readW(reinterpret_cast<int32_t>(address + 1), instr) |
|
2027 }; |
|
2028 double d; |
|
2029 memcpy(&d, data, 8); |
|
2030 set_d_register_from_double(reg, d); |
|
2031 } else { |
|
2032 int32_t data[2]; |
|
2033 double d = get_double_from_d_register(reg); |
|
2034 memcpy(data, &d, 8); |
|
2035 writeW(reinterpret_cast<int32_t>(address), data[0], instr); |
|
2036 writeW(reinterpret_cast<int32_t>(address + 1), data[1], instr); |
|
2037 } |
|
2038 address += 2; |
|
2039 } |
|
2040 } |
|
2041 MOZ_ASSERT(reinterpret_cast<intptr_t>(address) - operand_size == end_address); |
|
2042 if (instr->hasW()) |
|
2043 set_register(instr->rnValue(), rn_val); |
|
2044 } |
|
2045 |
|
2046 |
|
2047 // Note: With the code below we assume that all runtime calls return a 64 bits |
|
2048 // result. If they don't, the r1 result register contains a bogus value, which |
|
2049 // is fine because it is caller-saved. |
|
2050 typedef int64_t (*Prototype_General0)(); |
|
2051 typedef int64_t (*Prototype_General1)(int32_t arg0); |
|
2052 typedef int64_t (*Prototype_General2)(int32_t arg0, int32_t arg1); |
|
2053 typedef int64_t (*Prototype_General3)(int32_t arg0, int32_t arg1, int32_t arg2); |
|
2054 typedef int64_t (*Prototype_General4)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3); |
|
2055 typedef int64_t (*Prototype_General5)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
|
2056 int32_t arg4); |
|
2057 typedef int64_t (*Prototype_General6)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
|
2058 int32_t arg4, int32_t arg5); |
|
2059 typedef int64_t (*Prototype_General7)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
|
2060 int32_t arg4, int32_t arg5, int32_t arg6); |
|
2061 typedef int64_t (*Prototype_General8)(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, |
|
2062 int32_t arg4, int32_t arg5, int32_t arg6, int32_t arg7); |
|
2063 |
|
2064 typedef double (*Prototype_Double_None)(); |
|
2065 typedef double (*Prototype_Double_Double)(double arg0); |
|
2066 typedef double (*Prototype_Double_Int)(int32_t arg0); |
|
2067 typedef int32_t (*Prototype_Int_Double)(double arg0); |
|
2068 typedef float (*Prototype_Float32_Float32)(float arg0); |
|
2069 |
|
2070 typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1); |
|
2071 typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1); |
|
2072 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1); |
|
2073 typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1); |
|
2074 |
|
2075 |
|
2076 // Fill the volatile registers with scratch values. |
|
2077 // |
|
2078 // Some of the ABI calls assume that the float registers are not scratched, even |
|
2079 // though the ABI defines them as volatile - a performance optimization. These are |
|
2080 // all calls passing operands in integer registers, so for now the simulator does not |
|
2081 // scratch any float registers for these calls. Should try to narrow it further in |
|
2082 // future. |
|
2083 // |
|
2084 void |
|
2085 Simulator::scratchVolatileRegisters(bool scratchFloat) |
|
2086 { |
|
2087 int32_t scratch_value = 0xa5a5a5a5 ^ uint32_t(icount_); |
|
2088 set_register(r0, scratch_value); |
|
2089 set_register(r1, scratch_value); |
|
2090 set_register(r2, scratch_value); |
|
2091 set_register(r3, scratch_value); |
|
2092 set_register(r12, scratch_value); // Intra-Procedure-call scratch register |
|
2093 set_register(r14, scratch_value); // Link register |
|
2094 |
|
2095 if (scratchFloat) { |
|
2096 uint64_t scratch_value_d = 0x5a5a5a5a5a5a5a5aLU ^ uint64_t(icount_) ^ (uint64_t(icount_) << 30); |
|
2097 for (uint32_t i = d0; i < d8; i++) |
|
2098 set_d_register(i, &scratch_value_d); |
|
2099 for (uint32_t i = d16; i < FloatRegisters::Total; i++) |
|
2100 set_d_register(i, &scratch_value_d); |
|
2101 } |
|
2102 } |
|
2103 |
|
2104 // Software interrupt instructions are used by the simulator to call into C++. |
|
2105 void |
|
2106 Simulator::softwareInterrupt(SimInstruction *instr) |
|
2107 { |
|
2108 int svc = instr->svcValue(); |
|
2109 switch (svc) { |
|
2110 case kCallRtRedirected: { |
|
2111 Redirection *redirection = Redirection::FromSwiInstruction(instr); |
|
2112 int32_t arg0 = get_register(r0); |
|
2113 int32_t arg1 = get_register(r1); |
|
2114 int32_t arg2 = get_register(r2); |
|
2115 int32_t arg3 = get_register(r3); |
|
2116 int32_t *stack_pointer = reinterpret_cast<int32_t*>(get_register(sp)); |
|
2117 int32_t arg4 = stack_pointer[0]; |
|
2118 int32_t arg5 = stack_pointer[1]; |
|
2119 |
|
2120 int32_t saved_lr = get_register(lr); |
|
2121 intptr_t external = reinterpret_cast<intptr_t>(redirection->nativeFunction()); |
|
2122 |
|
2123 bool stack_aligned = (get_register(sp) & (StackAlignment - 1)) == 0; |
|
2124 if (!stack_aligned) { |
|
2125 fprintf(stderr, "Runtime call with unaligned stack!\n"); |
|
2126 MOZ_CRASH(); |
|
2127 } |
|
2128 |
|
2129 switch (redirection->type()) { |
|
2130 case Args_General0: { |
|
2131 Prototype_General0 target = reinterpret_cast<Prototype_General0>(external); |
|
2132 int64_t result = target(); |
|
2133 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2134 setCallResult(result); |
|
2135 break; |
|
2136 } |
|
2137 case Args_General1: { |
|
2138 Prototype_General1 target = reinterpret_cast<Prototype_General1>(external); |
|
2139 int64_t result = target(arg0); |
|
2140 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2141 setCallResult(result); |
|
2142 break; |
|
2143 } |
|
2144 case Args_General2: { |
|
2145 Prototype_General2 target = reinterpret_cast<Prototype_General2>(external); |
|
2146 int64_t result = target(arg0, arg1); |
|
2147 // The ARM backend makes calls to __aeabi_idivmod and __aeabi_uidivmod assuming |
|
2148 // that the float registers are non-volatile as a performance optimization, so the |
|
2149 // float registers must not be scratch when calling these. |
|
2150 bool scratchFloat = target != __aeabi_idivmod && target != __aeabi_uidivmod; |
|
2151 scratchVolatileRegisters(/* scratchFloat = */ scratchFloat); |
|
2152 setCallResult(result); |
|
2153 break; |
|
2154 } |
|
2155 case Args_General3: { |
|
2156 Prototype_General3 target = reinterpret_cast<Prototype_General3>(external); |
|
2157 int64_t result = target(arg0, arg1, arg2); |
|
2158 scratchVolatileRegisters(/* scratchFloat = true*/); |
|
2159 setCallResult(result); |
|
2160 break; |
|
2161 } |
|
2162 case Args_General4: { |
|
2163 Prototype_General4 target = reinterpret_cast<Prototype_General4>(external); |
|
2164 int64_t result = target(arg0, arg1, arg2, arg3); |
|
2165 scratchVolatileRegisters(/* scratchFloat = true*/); |
|
2166 setCallResult(result); |
|
2167 break; |
|
2168 } |
|
2169 case Args_General5: { |
|
2170 Prototype_General5 target = reinterpret_cast<Prototype_General5>(external); |
|
2171 int64_t result = target(arg0, arg1, arg2, arg3, arg4); |
|
2172 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2173 setCallResult(result); |
|
2174 break; |
|
2175 } |
|
2176 case Args_General6: { |
|
2177 Prototype_General6 target = reinterpret_cast<Prototype_General6>(external); |
|
2178 int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5); |
|
2179 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2180 setCallResult(result); |
|
2181 break; |
|
2182 } |
|
2183 case Args_General7: { |
|
2184 Prototype_General7 target = reinterpret_cast<Prototype_General7>(external); |
|
2185 int32_t arg6 = stack_pointer[2]; |
|
2186 int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6); |
|
2187 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2188 setCallResult(result); |
|
2189 break; |
|
2190 } |
|
2191 case Args_General8: { |
|
2192 Prototype_General8 target = reinterpret_cast<Prototype_General8>(external); |
|
2193 int32_t arg6 = stack_pointer[2]; |
|
2194 int32_t arg7 = stack_pointer[3]; |
|
2195 int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); |
|
2196 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2197 setCallResult(result); |
|
2198 break; |
|
2199 } |
|
2200 case Args_Double_None: { |
|
2201 Prototype_Double_None target = reinterpret_cast<Prototype_Double_None>(external); |
|
2202 double dresult = target(); |
|
2203 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2204 setCallResultDouble(dresult); |
|
2205 break; |
|
2206 } |
|
2207 case Args_Int_Double: { |
|
2208 double dval0, dval1; |
|
2209 int32_t ival; |
|
2210 getFpArgs(&dval0, &dval1, &ival); |
|
2211 Prototype_Int_Double target = reinterpret_cast<Prototype_Int_Double>(external); |
|
2212 int32_t res = target(dval0); |
|
2213 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2214 set_register(r0, res); |
|
2215 break; |
|
2216 } |
|
2217 case Args_Double_Double: { |
|
2218 double dval0, dval1; |
|
2219 int32_t ival; |
|
2220 getFpArgs(&dval0, &dval1, &ival); |
|
2221 Prototype_Double_Double target = reinterpret_cast<Prototype_Double_Double>(external); |
|
2222 double dresult = target(dval0); |
|
2223 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2224 setCallResultDouble(dresult); |
|
2225 break; |
|
2226 } |
|
2227 case Args_Float32_Float32: { |
|
2228 float fval0; |
|
2229 if (useHardFpABI()) |
|
2230 fval0 = get_float_from_s_register(0); |
|
2231 else |
|
2232 fval0 = mozilla::BitwiseCast<float>(arg0); |
|
2233 Prototype_Float32_Float32 target = reinterpret_cast<Prototype_Float32_Float32>(external); |
|
2234 float fresult = target(fval0); |
|
2235 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2236 setCallResultFloat(fresult); |
|
2237 break; |
|
2238 } |
|
2239 case Args_Double_Int: { |
|
2240 Prototype_Double_Int target = reinterpret_cast<Prototype_Double_Int>(external); |
|
2241 double dresult = target(arg0); |
|
2242 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2243 setCallResultDouble(dresult); |
|
2244 break; |
|
2245 } |
|
2246 case Args_Double_DoubleInt: { |
|
2247 double dval0, dval1; |
|
2248 int32_t ival; |
|
2249 getFpArgs(&dval0, &dval1, &ival); |
|
2250 Prototype_DoubleInt target = reinterpret_cast<Prototype_DoubleInt>(external); |
|
2251 double dresult = target(dval0, ival); |
|
2252 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2253 setCallResultDouble(dresult); |
|
2254 break; |
|
2255 } |
|
2256 case Args_Double_DoubleDouble: { |
|
2257 double dval0, dval1; |
|
2258 int32_t ival; |
|
2259 getFpArgs(&dval0, &dval1, &ival); |
|
2260 Prototype_Double_DoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDouble>(external); |
|
2261 double dresult = target(dval0, dval1); |
|
2262 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2263 setCallResultDouble(dresult); |
|
2264 break; |
|
2265 } |
|
2266 case Args_Double_IntDouble: { |
|
2267 int32_t ival = get_register(0); |
|
2268 double dval0; |
|
2269 if (useHardFpABI()) |
|
2270 dval0 = get_double_from_d_register(0); |
|
2271 else |
|
2272 dval0 = get_double_from_register_pair(2); |
|
2273 Prototype_Double_IntDouble target = reinterpret_cast<Prototype_Double_IntDouble>(external); |
|
2274 double dresult = target(ival, dval0); |
|
2275 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2276 setCallResultDouble(dresult); |
|
2277 break; |
|
2278 } |
|
2279 case Args_Int_IntDouble: { |
|
2280 int32_t ival = get_register(0); |
|
2281 double dval0; |
|
2282 if (useHardFpABI()) |
|
2283 dval0 = get_double_from_d_register(0); |
|
2284 else |
|
2285 dval0 = get_double_from_register_pair(2); |
|
2286 Prototype_Int_IntDouble target = reinterpret_cast<Prototype_Int_IntDouble>(external); |
|
2287 int32_t result = target(ival, dval0); |
|
2288 scratchVolatileRegisters(/* scratchFloat = true */); |
|
2289 set_register(r0, result); |
|
2290 break; |
|
2291 } |
|
2292 default: |
|
2293 MOZ_ASSUME_UNREACHABLE("call"); |
|
2294 } |
|
2295 |
|
2296 set_register(lr, saved_lr); |
|
2297 set_pc(get_register(lr)); |
|
2298 break; |
|
2299 } |
|
2300 case kBreakpoint: { |
|
2301 ArmDebugger dbg(this); |
|
2302 dbg.debug(); |
|
2303 break; |
|
2304 } |
|
2305 default: { // Stop uses all codes greater than 1 << 23. |
|
2306 if (svc >= (1 << 23)) { |
|
2307 uint32_t code = svc & kStopCodeMask; |
|
2308 if (isWatchedStop(code)) |
|
2309 increaseStopCounter(code); |
|
2310 |
|
2311 // Stop if it is enabled, otherwise go on jumping over the stop |
|
2312 // and the message address. |
|
2313 if (isEnabledStop(code)) { |
|
2314 ArmDebugger dbg(this); |
|
2315 dbg.stop(instr); |
|
2316 } else { |
|
2317 set_pc(get_pc() + 2 * SimInstruction::kInstrSize); |
|
2318 } |
|
2319 } else { |
|
2320 // This is not a valid svc code. |
|
2321 MOZ_CRASH(); |
|
2322 break; |
|
2323 } |
|
2324 } |
|
2325 } |
|
2326 } |
|
2327 |
|
2328 double |
|
2329 Simulator::canonicalizeNaN(double value) |
|
2330 { |
|
2331 return FPSCR_default_NaN_mode_ ? JS::CanonicalizeNaN(value) : value; |
|
2332 } |
|
2333 |
|
2334 // Stop helper functions. |
|
2335 bool |
|
2336 Simulator::isStopInstruction(SimInstruction *instr) |
|
2337 { |
|
2338 return (instr->bits(27, 24) == 0xF) && (instr->svcValue() >= kStopCode); |
|
2339 } |
|
2340 |
|
2341 bool Simulator::isWatchedStop(uint32_t code) |
|
2342 { |
|
2343 MOZ_ASSERT(code <= kMaxStopCode); |
|
2344 return code < kNumOfWatchedStops; |
|
2345 } |
|
2346 |
|
2347 bool |
|
2348 Simulator::isEnabledStop(uint32_t code) |
|
2349 { |
|
2350 MOZ_ASSERT(code <= kMaxStopCode); |
|
2351 // Unwatched stops are always enabled. |
|
2352 return !isWatchedStop(code) || !(watched_stops_[code].count & kStopDisabledBit); |
|
2353 } |
|
2354 |
|
2355 void |
|
2356 Simulator::enableStop(uint32_t code) |
|
2357 { |
|
2358 MOZ_ASSERT(isWatchedStop(code)); |
|
2359 if (!isEnabledStop(code)) |
|
2360 watched_stops_[code].count &= ~kStopDisabledBit; |
|
2361 } |
|
2362 |
|
2363 void |
|
2364 Simulator::disableStop(uint32_t code) |
|
2365 { |
|
2366 MOZ_ASSERT(isWatchedStop(code)); |
|
2367 if (isEnabledStop(code)) |
|
2368 watched_stops_[code].count |= kStopDisabledBit; |
|
2369 } |
|
2370 |
|
2371 void |
|
2372 Simulator::increaseStopCounter(uint32_t code) |
|
2373 { |
|
2374 MOZ_ASSERT(code <= kMaxStopCode); |
|
2375 MOZ_ASSERT(isWatchedStop(code)); |
|
2376 if ((watched_stops_[code].count & ~(1 << 31)) == 0x7fffffff) { |
|
2377 printf("Stop counter for code %i has overflowed.\n" |
|
2378 "Enabling this code and reseting the counter to 0.\n", code); |
|
2379 watched_stops_[code].count = 0; |
|
2380 enableStop(code); |
|
2381 } else { |
|
2382 watched_stops_[code].count++; |
|
2383 } |
|
2384 } |
|
2385 |
|
2386 // Print a stop status. |
|
2387 void |
|
2388 Simulator::printStopInfo(uint32_t code) |
|
2389 { |
|
2390 MOZ_ASSERT(code <= kMaxStopCode); |
|
2391 if (!isWatchedStop(code)) { |
|
2392 printf("Stop not watched."); |
|
2393 } else { |
|
2394 const char *state = isEnabledStop(code) ? "Enabled" : "Disabled"; |
|
2395 int32_t count = watched_stops_[code].count & ~kStopDisabledBit; |
|
2396 // Don't print the state of unused breakpoints. |
|
2397 if (count != 0) { |
|
2398 if (watched_stops_[code].desc) { |
|
2399 printf("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", |
|
2400 code, code, state, count, watched_stops_[code].desc); |
|
2401 } else { |
|
2402 printf("stop %i - 0x%x: \t%s, \tcounter = %i\n", |
|
2403 code, code, state, count); |
|
2404 } |
|
2405 } |
|
2406 } |
|
2407 } |
|
2408 |
|
2409 // Instruction types 0 and 1 are both rolled into one function because they |
|
2410 // only differ in the handling of the shifter_operand. |
|
2411 void |
|
2412 Simulator::decodeType01(SimInstruction *instr) |
|
2413 { |
|
2414 int type = instr->typeValue(); |
|
2415 if (type == 0 && instr->isSpecialType0()) { |
|
2416 // Multiply instruction or extra loads and stores. |
|
2417 if (instr->bits(7, 4) == 9) { |
|
2418 if (instr->bit(24) == 0) { |
|
2419 // Raw field decoding here. Multiply instructions have their Rd |
|
2420 // in funny places. |
|
2421 int rn = instr->rnValue(); |
|
2422 int rm = instr->rmValue(); |
|
2423 int rs = instr->rsValue(); |
|
2424 int32_t rs_val = get_register(rs); |
|
2425 int32_t rm_val = get_register(rm); |
|
2426 if (instr->bit(23) == 0) { |
|
2427 if (instr->bit(21) == 0) { |
|
2428 // The MUL instruction description (A 4.1.33) refers to Rd as being |
|
2429 // the destination for the operation, but it confusingly uses the |
|
2430 // Rn field to encode it. |
|
2431 int rd = rn; // Remap the rn field to the Rd register. |
|
2432 int32_t alu_out = rm_val * rs_val; |
|
2433 set_register(rd, alu_out); |
|
2434 if (instr->hasS()) |
|
2435 setNZFlags(alu_out); |
|
2436 } else { |
|
2437 int rd = instr->rdValue(); |
|
2438 int32_t acc_value = get_register(rd); |
|
2439 if (instr->bit(22) == 0) { |
|
2440 // The MLA instruction description (A 4.1.28) refers to the order |
|
2441 // of registers as "Rd, Rm, Rs, Rn". But confusingly it uses the |
|
2442 // Rn field to encode the Rd register and the Rd field to encode |
|
2443 // the Rn register. |
|
2444 int32_t mul_out = rm_val * rs_val; |
|
2445 int32_t result = acc_value + mul_out; |
|
2446 set_register(rn, result); |
|
2447 } else { |
|
2448 int32_t mul_out = rm_val * rs_val; |
|
2449 int32_t result = acc_value - mul_out; |
|
2450 set_register(rn, result); |
|
2451 } |
|
2452 } |
|
2453 } else { |
|
2454 // The signed/long multiply instructions use the terms RdHi and RdLo |
|
2455 // when referring to the target registers. They are mapped to the Rn |
|
2456 // and Rd fields as follows: |
|
2457 // RdLo == Rd |
|
2458 // RdHi == Rn (This is confusingly stored in variable rd here |
|
2459 // because the mul instruction from above uses the |
|
2460 // Rn field to encode the Rd register. Good luck figuring |
|
2461 // this out without reading the ARM instruction manual |
|
2462 // at a very detailed level.) |
|
2463 int rd_hi = rn; // Remap the rn field to the RdHi register. |
|
2464 int rd_lo = instr->rdValue(); |
|
2465 int32_t hi_res = 0; |
|
2466 int32_t lo_res = 0; |
|
2467 if (instr->bit(22) == 1) { |
|
2468 int64_t left_op = static_cast<int32_t>(rm_val); |
|
2469 int64_t right_op = static_cast<int32_t>(rs_val); |
|
2470 uint64_t result = left_op * right_op; |
|
2471 hi_res = static_cast<int32_t>(result >> 32); |
|
2472 lo_res = static_cast<int32_t>(result & 0xffffffff); |
|
2473 } else { |
|
2474 // unsigned multiply |
|
2475 uint64_t left_op = static_cast<uint32_t>(rm_val); |
|
2476 uint64_t right_op = static_cast<uint32_t>(rs_val); |
|
2477 uint64_t result = left_op * right_op; |
|
2478 hi_res = static_cast<int32_t>(result >> 32); |
|
2479 lo_res = static_cast<int32_t>(result & 0xffffffff); |
|
2480 } |
|
2481 set_register(rd_lo, lo_res); |
|
2482 set_register(rd_hi, hi_res); |
|
2483 if (instr->hasS()) |
|
2484 MOZ_CRASH(); |
|
2485 } |
|
2486 } else { |
|
2487 MOZ_CRASH(); // Not used atm. |
|
2488 } |
|
2489 } else { |
|
2490 // extra load/store instructions |
|
2491 int rd = instr->rdValue(); |
|
2492 int rn = instr->rnValue(); |
|
2493 int32_t rn_val = get_register(rn); |
|
2494 int32_t addr = 0; |
|
2495 if (instr->bit(22) == 0) { |
|
2496 int rm = instr->rmValue(); |
|
2497 int32_t rm_val = get_register(rm); |
|
2498 switch (instr->PUField()) { |
|
2499 case da_x: |
|
2500 MOZ_ASSERT(!instr->hasW()); |
|
2501 addr = rn_val; |
|
2502 rn_val -= rm_val; |
|
2503 set_register(rn, rn_val); |
|
2504 break; |
|
2505 case ia_x: |
|
2506 MOZ_ASSERT(!instr->hasW()); |
|
2507 addr = rn_val; |
|
2508 rn_val += rm_val; |
|
2509 set_register(rn, rn_val); |
|
2510 break; |
|
2511 case db_x: |
|
2512 rn_val -= rm_val; |
|
2513 addr = rn_val; |
|
2514 if (instr->hasW()) |
|
2515 set_register(rn, rn_val); |
|
2516 break; |
|
2517 case ib_x: |
|
2518 rn_val += rm_val; |
|
2519 addr = rn_val; |
|
2520 if (instr->hasW()) |
|
2521 set_register(rn, rn_val); |
|
2522 break; |
|
2523 default: |
|
2524 // The PU field is a 2-bit field. |
|
2525 MOZ_CRASH(); |
|
2526 break; |
|
2527 } |
|
2528 } else { |
|
2529 int32_t imm_val = (instr->immedHValue() << 4) | instr->immedLValue(); |
|
2530 switch (instr->PUField()) { |
|
2531 case da_x: |
|
2532 MOZ_ASSERT(!instr->hasW()); |
|
2533 addr = rn_val; |
|
2534 rn_val -= imm_val; |
|
2535 set_register(rn, rn_val); |
|
2536 break; |
|
2537 case ia_x: |
|
2538 MOZ_ASSERT(!instr->hasW()); |
|
2539 addr = rn_val; |
|
2540 rn_val += imm_val; |
|
2541 set_register(rn, rn_val); |
|
2542 break; |
|
2543 case db_x: |
|
2544 rn_val -= imm_val; |
|
2545 addr = rn_val; |
|
2546 if (instr->hasW()) |
|
2547 set_register(rn, rn_val); |
|
2548 break; |
|
2549 case ib_x: |
|
2550 rn_val += imm_val; |
|
2551 addr = rn_val; |
|
2552 if (instr->hasW()) |
|
2553 set_register(rn, rn_val); |
|
2554 break; |
|
2555 default: |
|
2556 // The PU field is a 2-bit field. |
|
2557 MOZ_CRASH(); |
|
2558 break; |
|
2559 } |
|
2560 } |
|
2561 if ((instr->bits(7, 4) & 0xd) == 0xd && instr->bit(20) == 0) { |
|
2562 MOZ_ASSERT((rd % 2) == 0); |
|
2563 if (instr->hasH()) { |
|
2564 // The strd instruction. |
|
2565 int32_t value1 = get_register(rd); |
|
2566 int32_t value2 = get_register(rd+1); |
|
2567 writeDW(addr, value1, value2); |
|
2568 } else { |
|
2569 // The ldrd instruction. |
|
2570 int *rn_data = readDW(addr); |
|
2571 set_dw_register(rd, rn_data); |
|
2572 } |
|
2573 } else if (instr->hasH()) { |
|
2574 if (instr->hasSign()) { |
|
2575 if (instr->hasL()) { |
|
2576 int16_t val = readH(addr, instr); |
|
2577 set_register(rd, val); |
|
2578 } else { |
|
2579 int16_t val = get_register(rd); |
|
2580 writeH(addr, val, instr); |
|
2581 } |
|
2582 } else { |
|
2583 if (instr->hasL()) { |
|
2584 uint16_t val = readHU(addr, instr); |
|
2585 set_register(rd, val); |
|
2586 } else { |
|
2587 uint16_t val = get_register(rd); |
|
2588 writeH(addr, val, instr); |
|
2589 } |
|
2590 } |
|
2591 } else { |
|
2592 // signed byte loads |
|
2593 MOZ_ASSERT(instr->hasSign()); |
|
2594 MOZ_ASSERT(instr->hasL()); |
|
2595 int8_t val = readB(addr); |
|
2596 set_register(rd, val); |
|
2597 } |
|
2598 return; |
|
2599 } |
|
2600 } else if ((type == 0) && instr->isMiscType0()) { |
|
2601 if (instr->bits(7, 4) == 0) { |
|
2602 if (instr->bit(21) == 0) { |
|
2603 // mrs |
|
2604 int rd = instr->rdValue(); |
|
2605 uint32_t flags; |
|
2606 if (instr->bit(22) == 0) { |
|
2607 // CPSR. Note: The Q flag is not yet implemented! |
|
2608 flags = (n_flag_ << 31) | |
|
2609 (z_flag_ << 30) | |
|
2610 (c_flag_ << 29) | |
|
2611 (v_flag_ << 28); |
|
2612 } else { |
|
2613 // SPSR |
|
2614 MOZ_CRASH(); |
|
2615 } |
|
2616 set_register(rd, flags); |
|
2617 } else { |
|
2618 // msr |
|
2619 if (instr->bits(27, 23) == 2) { |
|
2620 // Register operand. For now we only emit mask 0b1100. |
|
2621 int rm = instr->rmValue(); |
|
2622 uint32_t mask = instr->bits(19, 16); |
|
2623 MOZ_ASSERT(mask == (3 << 2)); |
|
2624 |
|
2625 uint32_t flags = get_register(rm); |
|
2626 n_flag_ = (flags >> 31) & 1; |
|
2627 z_flag_ = (flags >> 30) & 1; |
|
2628 c_flag_ = (flags >> 29) & 1; |
|
2629 v_flag_ = (flags >> 28) & 1; |
|
2630 } else { |
|
2631 MOZ_CRASH(); |
|
2632 } |
|
2633 } |
|
2634 } else if (instr->bits(22, 21) == 1) { |
|
2635 int rm = instr->rmValue(); |
|
2636 switch (instr->bits(7, 4)) { |
|
2637 case 1: // BX |
|
2638 set_pc(get_register(rm)); |
|
2639 break; |
|
2640 case 3: { // BLX |
|
2641 uint32_t old_pc = get_pc(); |
|
2642 set_pc(get_register(rm)); |
|
2643 set_register(lr, old_pc + SimInstruction::kInstrSize); |
|
2644 break; |
|
2645 } |
|
2646 case 7: { // BKPT |
|
2647 ArmDebugger dbg(this); |
|
2648 printf("Simulator hit BKPT.\n"); |
|
2649 dbg.debug(); |
|
2650 break; |
|
2651 } |
|
2652 default: |
|
2653 MOZ_CRASH(); |
|
2654 } |
|
2655 } else if (instr->bits(22, 21) == 3) { |
|
2656 int rm = instr->rmValue(); |
|
2657 int rd = instr->rdValue(); |
|
2658 switch (instr->bits(7, 4)) { |
|
2659 case 1: { // CLZ |
|
2660 uint32_t bits = get_register(rm); |
|
2661 int leading_zeros = 0; |
|
2662 if (bits == 0) |
|
2663 leading_zeros = 32; |
|
2664 else |
|
2665 leading_zeros = mozilla::CountLeadingZeroes32(bits); |
|
2666 set_register(rd, leading_zeros); |
|
2667 break; |
|
2668 } |
|
2669 default: |
|
2670 MOZ_CRASH(); |
|
2671 break; |
|
2672 } |
|
2673 } else { |
|
2674 printf("%08x\n", instr->instructionBits()); |
|
2675 MOZ_CRASH(); |
|
2676 } |
|
2677 } else if ((type == 1) && instr->isNopType1()) { |
|
2678 // NOP. |
|
2679 } else { |
|
2680 int rd = instr->rdValue(); |
|
2681 int rn = instr->rnValue(); |
|
2682 int32_t rn_val = get_register(rn); |
|
2683 int32_t shifter_operand = 0; |
|
2684 bool shifter_carry_out = 0; |
|
2685 if (type == 0) { |
|
2686 shifter_operand = getShiftRm(instr, &shifter_carry_out); |
|
2687 } else { |
|
2688 MOZ_ASSERT(instr->typeValue() == 1); |
|
2689 shifter_operand = getImm(instr, &shifter_carry_out); |
|
2690 } |
|
2691 int32_t alu_out; |
|
2692 switch (instr->opcodeField()) { |
|
2693 case op_and: |
|
2694 alu_out = rn_val & shifter_operand; |
|
2695 set_register(rd, alu_out); |
|
2696 if (instr->hasS()) { |
|
2697 setNZFlags(alu_out); |
|
2698 setCFlag(shifter_carry_out); |
|
2699 } |
|
2700 break; |
|
2701 case op_eor: |
|
2702 alu_out = rn_val ^ shifter_operand; |
|
2703 set_register(rd, alu_out); |
|
2704 if (instr->hasS()) { |
|
2705 setNZFlags(alu_out); |
|
2706 setCFlag(shifter_carry_out); |
|
2707 } |
|
2708 break; |
|
2709 case op_sub: |
|
2710 alu_out = rn_val - shifter_operand; |
|
2711 set_register(rd, alu_out); |
|
2712 if (instr->hasS()) { |
|
2713 setNZFlags(alu_out); |
|
2714 setCFlag(!borrowFrom(rn_val, shifter_operand)); |
|
2715 setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, false)); |
|
2716 } |
|
2717 break; |
|
2718 case op_rsb: |
|
2719 alu_out = shifter_operand - rn_val; |
|
2720 set_register(rd, alu_out); |
|
2721 if (instr->hasS()) { |
|
2722 setNZFlags(alu_out); |
|
2723 setCFlag(!borrowFrom(shifter_operand, rn_val)); |
|
2724 setVFlag(overflowFrom(alu_out, shifter_operand, rn_val, false)); |
|
2725 } |
|
2726 break; |
|
2727 case op_add: |
|
2728 alu_out = rn_val + shifter_operand; |
|
2729 set_register(rd, alu_out); |
|
2730 if (instr->hasS()) { |
|
2731 setNZFlags(alu_out); |
|
2732 setCFlag(carryFrom(rn_val, shifter_operand)); |
|
2733 setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); |
|
2734 } |
|
2735 break; |
|
2736 case op_adc: |
|
2737 alu_out = rn_val + shifter_operand + getCarry(); |
|
2738 set_register(rd, alu_out); |
|
2739 if (instr->hasS()) { |
|
2740 setNZFlags(alu_out); |
|
2741 setCFlag(carryFrom(rn_val, shifter_operand, getCarry())); |
|
2742 setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); |
|
2743 } |
|
2744 break; |
|
2745 case op_sbc: |
|
2746 case op_rsc: |
|
2747 MOZ_CRASH(); |
|
2748 break; |
|
2749 case op_tst: |
|
2750 if (instr->hasS()) { |
|
2751 alu_out = rn_val & shifter_operand; |
|
2752 setNZFlags(alu_out); |
|
2753 setCFlag(shifter_carry_out); |
|
2754 } else { |
|
2755 alu_out = instr->immedMovwMovtValue(); |
|
2756 set_register(rd, alu_out); |
|
2757 } |
|
2758 break; |
|
2759 case op_teq: |
|
2760 if (instr->hasS()) { |
|
2761 alu_out = rn_val ^ shifter_operand; |
|
2762 setNZFlags(alu_out); |
|
2763 setCFlag(shifter_carry_out); |
|
2764 } else { |
|
2765 // Other instructions matching this pattern are handled in the |
|
2766 // miscellaneous instructions part above. |
|
2767 MOZ_CRASH(); |
|
2768 } |
|
2769 break; |
|
2770 case op_cmp: |
|
2771 if (instr->hasS()) { |
|
2772 alu_out = rn_val - shifter_operand; |
|
2773 setNZFlags(alu_out); |
|
2774 setCFlag(!borrowFrom(rn_val, shifter_operand)); |
|
2775 setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, false)); |
|
2776 } else { |
|
2777 alu_out = (get_register(rd) & 0xffff) | |
|
2778 (instr->immedMovwMovtValue() << 16); |
|
2779 set_register(rd, alu_out); |
|
2780 } |
|
2781 break; |
|
2782 case op_cmn: |
|
2783 if (instr->hasS()) { |
|
2784 alu_out = rn_val + shifter_operand; |
|
2785 setNZFlags(alu_out); |
|
2786 setCFlag(carryFrom(rn_val, shifter_operand)); |
|
2787 setVFlag(overflowFrom(alu_out, rn_val, shifter_operand, true)); |
|
2788 } else { |
|
2789 // Other instructions matching this pattern are handled in the |
|
2790 // miscellaneous instructions part above. |
|
2791 MOZ_CRASH(); |
|
2792 } |
|
2793 break; |
|
2794 case op_orr: |
|
2795 alu_out = rn_val | shifter_operand; |
|
2796 set_register(rd, alu_out); |
|
2797 if (instr->hasS()) { |
|
2798 setNZFlags(alu_out); |
|
2799 setCFlag(shifter_carry_out); |
|
2800 } |
|
2801 break; |
|
2802 case op_mov: |
|
2803 alu_out = shifter_operand; |
|
2804 set_register(rd, alu_out); |
|
2805 if (instr->hasS()) { |
|
2806 setNZFlags(alu_out); |
|
2807 setCFlag(shifter_carry_out); |
|
2808 } |
|
2809 break; |
|
2810 case op_bic: |
|
2811 alu_out = rn_val & ~shifter_operand; |
|
2812 set_register(rd, alu_out); |
|
2813 if (instr->hasS()) { |
|
2814 setNZFlags(alu_out); |
|
2815 setCFlag(shifter_carry_out); |
|
2816 } |
|
2817 break; |
|
2818 case op_mvn: |
|
2819 alu_out = ~shifter_operand; |
|
2820 set_register(rd, alu_out); |
|
2821 if (instr->hasS()) { |
|
2822 setNZFlags(alu_out); |
|
2823 setCFlag(shifter_carry_out); |
|
2824 } |
|
2825 break; |
|
2826 default: |
|
2827 MOZ_CRASH(); |
|
2828 break; |
|
2829 } |
|
2830 } |
|
2831 } |
|
2832 |
|
2833 void |
|
2834 Simulator::decodeType2(SimInstruction *instr) |
|
2835 { |
|
2836 int rd = instr->rdValue(); |
|
2837 int rn = instr->rnValue(); |
|
2838 int32_t rn_val = get_register(rn); |
|
2839 int32_t im_val = instr->offset12Value(); |
|
2840 int32_t addr = 0; |
|
2841 switch (instr->PUField()) { |
|
2842 case da_x: |
|
2843 MOZ_ASSERT(!instr->hasW()); |
|
2844 addr = rn_val; |
|
2845 rn_val -= im_val; |
|
2846 set_register(rn, rn_val); |
|
2847 break; |
|
2848 case ia_x: |
|
2849 MOZ_ASSERT(!instr->hasW()); |
|
2850 addr = rn_val; |
|
2851 rn_val += im_val; |
|
2852 set_register(rn, rn_val); |
|
2853 break; |
|
2854 case db_x: |
|
2855 rn_val -= im_val; |
|
2856 addr = rn_val; |
|
2857 if (instr->hasW()) |
|
2858 set_register(rn, rn_val); |
|
2859 break; |
|
2860 case ib_x: |
|
2861 rn_val += im_val; |
|
2862 addr = rn_val; |
|
2863 if (instr->hasW()) |
|
2864 set_register(rn, rn_val); |
|
2865 break; |
|
2866 default: |
|
2867 MOZ_CRASH(); |
|
2868 break; |
|
2869 } |
|
2870 if (instr->hasB()) { |
|
2871 if (instr->hasL()) { |
|
2872 uint8_t val = readBU(addr); |
|
2873 set_register(rd, val); |
|
2874 } else { |
|
2875 uint8_t val = get_register(rd); |
|
2876 writeB(addr, val); |
|
2877 } |
|
2878 } else { |
|
2879 if (instr->hasL()) |
|
2880 set_register(rd, readW(addr, instr)); |
|
2881 else |
|
2882 writeW(addr, get_register(rd), instr); |
|
2883 } |
|
2884 } |
|
2885 |
|
2886 void |
|
2887 Simulator::decodeType3(SimInstruction *instr) |
|
2888 { |
|
2889 int rd = instr->rdValue(); |
|
2890 int rn = instr->rnValue(); |
|
2891 int32_t rn_val = get_register(rn); |
|
2892 bool shifter_carry_out = 0; |
|
2893 int32_t shifter_operand = getShiftRm(instr, &shifter_carry_out); |
|
2894 int32_t addr = 0; |
|
2895 switch (instr->PUField()) { |
|
2896 case da_x: |
|
2897 MOZ_ASSERT(!instr->hasW()); |
|
2898 MOZ_CRASH(); |
|
2899 break; |
|
2900 case ia_x: { |
|
2901 if (instr->bit(4) == 0) { |
|
2902 // Memop. |
|
2903 } else { |
|
2904 if (instr->bit(5) == 0) { |
|
2905 switch (instr->bits(22, 21)) { |
|
2906 case 0: |
|
2907 if (instr->bit(20) == 0) { |
|
2908 if (instr->bit(6) == 0) { |
|
2909 // Pkhbt. |
|
2910 uint32_t rn_val = get_register(rn); |
|
2911 uint32_t rm_val = get_register(instr->rmValue()); |
|
2912 int32_t shift = instr->bits(11, 7); |
|
2913 rm_val <<= shift; |
|
2914 set_register(rd, (rn_val & 0xFFFF) | (rm_val & 0xFFFF0000U)); |
|
2915 } else { |
|
2916 // Pkhtb. |
|
2917 uint32_t rn_val = get_register(rn); |
|
2918 int32_t rm_val = get_register(instr->rmValue()); |
|
2919 int32_t shift = instr->bits(11, 7); |
|
2920 if (shift == 0) |
|
2921 shift = 32; |
|
2922 rm_val >>= shift; |
|
2923 set_register(rd, (rn_val & 0xFFFF0000U) | (rm_val & 0xFFFF)); |
|
2924 } |
|
2925 } else { |
|
2926 MOZ_CRASH(); |
|
2927 } |
|
2928 break; |
|
2929 case 1: |
|
2930 MOZ_CRASH(); |
|
2931 break; |
|
2932 case 2: |
|
2933 MOZ_CRASH(); |
|
2934 break; |
|
2935 case 3: { |
|
2936 // Usat. |
|
2937 int32_t sat_pos = instr->bits(20, 16); |
|
2938 int32_t sat_val = (1 << sat_pos) - 1; |
|
2939 int32_t shift = instr->bits(11, 7); |
|
2940 int32_t shift_type = instr->bit(6); |
|
2941 int32_t rm_val = get_register(instr->rmValue()); |
|
2942 if (shift_type == 0) // LSL |
|
2943 rm_val <<= shift; |
|
2944 else // ASR |
|
2945 rm_val >>= shift; |
|
2946 |
|
2947 // If saturation occurs, the Q flag should be set in the CPSR. |
|
2948 // There is no Q flag yet, and no instruction (MRS) to read the |
|
2949 // CPSR directly. |
|
2950 if (rm_val > sat_val) |
|
2951 rm_val = sat_val; |
|
2952 else if (rm_val < 0) |
|
2953 rm_val = 0; |
|
2954 set_register(rd, rm_val); |
|
2955 break; |
|
2956 } |
|
2957 } |
|
2958 } else { |
|
2959 switch (instr->bits(22, 21)) { |
|
2960 case 0: |
|
2961 MOZ_CRASH(); |
|
2962 break; |
|
2963 case 1: |
|
2964 MOZ_CRASH(); |
|
2965 break; |
|
2966 case 2: |
|
2967 if ((instr->bit(20) == 0) && (instr->bits(9, 6) == 1)) { |
|
2968 if (instr->bits(19, 16) == 0xF) { |
|
2969 // Uxtb16. |
|
2970 uint32_t rm_val = get_register(instr->rmValue()); |
|
2971 int32_t rotate = instr->bits(11, 10); |
|
2972 switch (rotate) { |
|
2973 case 0: |
|
2974 break; |
|
2975 case 1: |
|
2976 rm_val = (rm_val >> 8) | (rm_val << 24); |
|
2977 break; |
|
2978 case 2: |
|
2979 rm_val = (rm_val >> 16) | (rm_val << 16); |
|
2980 break; |
|
2981 case 3: |
|
2982 rm_val = (rm_val >> 24) | (rm_val << 8); |
|
2983 break; |
|
2984 } |
|
2985 set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); |
|
2986 } else { |
|
2987 MOZ_CRASH(); |
|
2988 } |
|
2989 } else { |
|
2990 MOZ_CRASH(); |
|
2991 } |
|
2992 break; |
|
2993 case 3: |
|
2994 if ((instr->bit(20) == 0) && (instr->bits(9, 6) == 1)) { |
|
2995 if (instr->bits(19, 16) == 0xF) { |
|
2996 // Uxtb. |
|
2997 uint32_t rm_val = get_register(instr->rmValue()); |
|
2998 int32_t rotate = instr->bits(11, 10); |
|
2999 switch (rotate) { |
|
3000 case 0: |
|
3001 break; |
|
3002 case 1: |
|
3003 rm_val = (rm_val >> 8) | (rm_val << 24); |
|
3004 break; |
|
3005 case 2: |
|
3006 rm_val = (rm_val >> 16) | (rm_val << 16); |
|
3007 break; |
|
3008 case 3: |
|
3009 rm_val = (rm_val >> 24) | (rm_val << 8); |
|
3010 break; |
|
3011 } |
|
3012 set_register(rd, (rm_val & 0xFF)); |
|
3013 } else { |
|
3014 // Uxtab. |
|
3015 uint32_t rn_val = get_register(rn); |
|
3016 uint32_t rm_val = get_register(instr->rmValue()); |
|
3017 int32_t rotate = instr->bits(11, 10); |
|
3018 switch (rotate) { |
|
3019 case 0: |
|
3020 break; |
|
3021 case 1: |
|
3022 rm_val = (rm_val >> 8) | (rm_val << 24); |
|
3023 break; |
|
3024 case 2: |
|
3025 rm_val = (rm_val >> 16) | (rm_val << 16); |
|
3026 break; |
|
3027 case 3: |
|
3028 rm_val = (rm_val >> 24) | (rm_val << 8); |
|
3029 break; |
|
3030 } |
|
3031 set_register(rd, rn_val + (rm_val & 0xFF)); |
|
3032 } |
|
3033 } else { |
|
3034 MOZ_CRASH(); |
|
3035 } |
|
3036 break; |
|
3037 } |
|
3038 } |
|
3039 return; |
|
3040 } |
|
3041 break; |
|
3042 } |
|
3043 case db_x: { // sudiv |
|
3044 if (instr->bit(22) == 0x0 && instr->bit(20) == 0x1 && |
|
3045 instr->bits(15,12) == 0x0f && instr->bits(7, 4) == 0x1) { |
|
3046 if (!instr->hasW()) { |
|
3047 // sdiv (in V8 notation matching ARM ISA format) rn = rm/rs |
|
3048 int rm = instr->rmValue(); |
|
3049 int32_t rm_val = get_register(rm); |
|
3050 int rs = instr->rsValue(); |
|
3051 int32_t rs_val = get_register(rs); |
|
3052 int32_t ret_val = 0; |
|
3053 MOZ_ASSERT(rs_val != 0); |
|
3054 if ((rm_val == INT32_MIN) && (rs_val == -1)) |
|
3055 ret_val = INT32_MIN; |
|
3056 else |
|
3057 ret_val = rm_val / rs_val; |
|
3058 set_register(rn, ret_val); |
|
3059 return; |
|
3060 } else { |
|
3061 // udiv (in V8 notation matching ARM ISA format) rn = rm/rs |
|
3062 int rm = instr->rmValue(); |
|
3063 uint32_t rm_val = get_register(rm); |
|
3064 int rs = instr->rsValue(); |
|
3065 uint32_t rs_val = get_register(rs); |
|
3066 uint32_t ret_val = 0; |
|
3067 MOZ_ASSERT(rs_val != 0); |
|
3068 ret_val = rm_val / rs_val; |
|
3069 set_register(rn, ret_val); |
|
3070 return; |
|
3071 } |
|
3072 } |
|
3073 |
|
3074 addr = rn_val - shifter_operand; |
|
3075 if (instr->hasW()) |
|
3076 set_register(rn, addr); |
|
3077 break; |
|
3078 } |
|
3079 case ib_x: { |
|
3080 if (instr->hasW() && (instr->bits(6, 4) == 0x5)) { |
|
3081 uint32_t widthminus1 = static_cast<uint32_t>(instr->bits(20, 16)); |
|
3082 uint32_t lsbit = static_cast<uint32_t>(instr->bits(11, 7)); |
|
3083 uint32_t msbit = widthminus1 + lsbit; |
|
3084 if (msbit <= 31) { |
|
3085 if (instr->bit(22)) { |
|
3086 // ubfx - unsigned bitfield extract. |
|
3087 uint32_t rm_val = static_cast<uint32_t>(get_register(instr->rmValue())); |
|
3088 uint32_t extr_val = rm_val << (31 - msbit); |
|
3089 extr_val = extr_val >> (31 - widthminus1); |
|
3090 set_register(instr->rdValue(), extr_val); |
|
3091 } else { |
|
3092 // sbfx - signed bitfield extract. |
|
3093 int32_t rm_val = get_register(instr->rmValue()); |
|
3094 int32_t extr_val = rm_val << (31 - msbit); |
|
3095 extr_val = extr_val >> (31 - widthminus1); |
|
3096 set_register(instr->rdValue(), extr_val); |
|
3097 } |
|
3098 } else { |
|
3099 MOZ_CRASH(); |
|
3100 } |
|
3101 return; |
|
3102 } else if (!instr->hasW() && (instr->bits(6, 4) == 0x1)) { |
|
3103 uint32_t lsbit = static_cast<uint32_t>(instr->bits(11, 7)); |
|
3104 uint32_t msbit = static_cast<uint32_t>(instr->bits(20, 16)); |
|
3105 if (msbit >= lsbit) { |
|
3106 // bfc or bfi - bitfield clear/insert. |
|
3107 uint32_t rd_val = |
|
3108 static_cast<uint32_t>(get_register(instr->rdValue())); |
|
3109 uint32_t bitcount = msbit - lsbit + 1; |
|
3110 uint32_t mask = (1 << bitcount) - 1; |
|
3111 rd_val &= ~(mask << lsbit); |
|
3112 if (instr->rmValue() != 15) { |
|
3113 // bfi - bitfield insert. |
|
3114 uint32_t rm_val = |
|
3115 static_cast<uint32_t>(get_register(instr->rmValue())); |
|
3116 rm_val &= mask; |
|
3117 rd_val |= rm_val << lsbit; |
|
3118 } |
|
3119 set_register(instr->rdValue(), rd_val); |
|
3120 } else { |
|
3121 MOZ_CRASH(); |
|
3122 } |
|
3123 return; |
|
3124 } else { |
|
3125 addr = rn_val + shifter_operand; |
|
3126 if (instr->hasW()) |
|
3127 set_register(rn, addr); |
|
3128 } |
|
3129 break; |
|
3130 } |
|
3131 default: |
|
3132 MOZ_CRASH(); |
|
3133 break; |
|
3134 } |
|
3135 if (instr->hasB()) { |
|
3136 if (instr->hasL()) { |
|
3137 uint8_t byte = readB(addr); |
|
3138 set_register(rd, byte); |
|
3139 } else { |
|
3140 uint8_t byte = get_register(rd); |
|
3141 writeB(addr, byte); |
|
3142 } |
|
3143 } else { |
|
3144 if (instr->hasL()) |
|
3145 set_register(rd, readW(addr, instr)); |
|
3146 else |
|
3147 writeW(addr, get_register(rd), instr); |
|
3148 } |
|
3149 } |
|
3150 |
|
3151 void |
|
3152 Simulator::decodeType4(SimInstruction *instr) |
|
3153 { |
|
3154 MOZ_ASSERT(instr->bit(22) == 0); // Only allowed to be set in privileged mode. |
|
3155 bool load = instr->hasL(); |
|
3156 handleRList(instr, load); |
|
3157 } |
|
3158 |
|
3159 void |
|
3160 Simulator::decodeType5(SimInstruction *instr) |
|
3161 { |
|
3162 int off = instr->sImmed24Value() << 2; |
|
3163 intptr_t pc_address = get_pc(); |
|
3164 if (instr->hasLink()) |
|
3165 set_register(lr, pc_address + SimInstruction::kInstrSize); |
|
3166 int pc_reg = get_register(pc); |
|
3167 set_pc(pc_reg + off); |
|
3168 } |
|
3169 |
|
3170 void |
|
3171 Simulator::decodeType6(SimInstruction *instr) |
|
3172 { |
|
3173 decodeType6CoprocessorIns(instr); |
|
3174 } |
|
3175 |
|
3176 void |
|
3177 Simulator::decodeType7(SimInstruction *instr) |
|
3178 { |
|
3179 if (instr->bit(24) == 1) |
|
3180 softwareInterrupt(instr); |
|
3181 else |
|
3182 decodeTypeVFP(instr); |
|
3183 } |
|
3184 |
|
3185 void |
|
3186 Simulator::decodeTypeVFP(SimInstruction *instr) |
|
3187 { |
|
3188 MOZ_ASSERT(instr->typeValue() == 7 && instr->bit(24) == 0); |
|
3189 MOZ_ASSERT(instr->bits(11, 9) == 0x5); |
|
3190 |
|
3191 // Obtain double precision register codes. |
|
3192 VFPRegPrecision precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; |
|
3193 int vm = instr->VFPMRegValue(precision); |
|
3194 int vd = instr->VFPDRegValue(precision); |
|
3195 int vn = instr->VFPNRegValue(precision); |
|
3196 |
|
3197 if (instr->bit(4) == 0) { |
|
3198 if (instr->opc1Value() == 0x7) { |
|
3199 // Other data processing instructions |
|
3200 if ((instr->opc2Value() == 0x0) && (instr->opc3Value() == 0x1)) { |
|
3201 // vmov register to register. |
|
3202 if (instr->szValue() == 0x1) { |
|
3203 int m = instr->VFPMRegValue(kDoublePrecision); |
|
3204 int d = instr->VFPDRegValue(kDoublePrecision); |
|
3205 set_d_register_from_double(d, get_double_from_d_register(m)); |
|
3206 } else { |
|
3207 int m = instr->VFPMRegValue(kSinglePrecision); |
|
3208 int d = instr->VFPDRegValue(kSinglePrecision); |
|
3209 set_s_register_from_float(d, get_float_from_s_register(m)); |
|
3210 } |
|
3211 } else if ((instr->opc2Value() == 0x0) && (instr->opc3Value() == 0x3)) { |
|
3212 // vabs |
|
3213 if (instr->szValue() == 0x1) { |
|
3214 double dm_value = get_double_from_d_register(vm); |
|
3215 double dd_value = std::fabs(dm_value); |
|
3216 dd_value = canonicalizeNaN(dd_value); |
|
3217 set_d_register_from_double(vd, dd_value); |
|
3218 } else { |
|
3219 float fm_value = get_float_from_s_register(vm); |
|
3220 float fd_value = std::fabs(fm_value); |
|
3221 fd_value = canonicalizeNaN(fd_value); |
|
3222 set_s_register_from_float(vd, fd_value); |
|
3223 } |
|
3224 } else if ((instr->opc2Value() == 0x1) && (instr->opc3Value() == 0x1)) { |
|
3225 // vneg |
|
3226 if (instr->szValue() == 0x1) { |
|
3227 double dm_value = get_double_from_d_register(vm); |
|
3228 double dd_value = -dm_value; |
|
3229 dd_value = canonicalizeNaN(dd_value); |
|
3230 set_d_register_from_double(vd, dd_value); |
|
3231 } else { |
|
3232 float fm_value = get_float_from_s_register(vm); |
|
3233 float fd_value = -fm_value; |
|
3234 fd_value = canonicalizeNaN(fd_value); |
|
3235 set_s_register_from_float(vd, fd_value); |
|
3236 } |
|
3237 } else if ((instr->opc2Value() == 0x7) && (instr->opc3Value() == 0x3)) { |
|
3238 decodeVCVTBetweenDoubleAndSingle(instr); |
|
3239 } else if ((instr->opc2Value() == 0x8) && (instr->opc3Value() & 0x1)) { |
|
3240 decodeVCVTBetweenFloatingPointAndInteger(instr); |
|
3241 } else if ((instr->opc2Value() == 0xA) && (instr->opc3Value() == 0x3) && |
|
3242 (instr->bit(8) == 1)) { |
|
3243 // vcvt.f64.s32 Dd, Dd, #<fbits> |
|
3244 int fraction_bits = 32 - ((instr->bits(3, 0) << 1) | instr->bit(5)); |
|
3245 int fixed_value = get_sinteger_from_s_register(vd * 2); |
|
3246 double divide = 1 << fraction_bits; |
|
3247 set_d_register_from_double(vd, fixed_value / divide); |
|
3248 } else if (((instr->opc2Value() >> 1) == 0x6) && |
|
3249 (instr->opc3Value() & 0x1)) { |
|
3250 decodeVCVTBetweenFloatingPointAndInteger(instr); |
|
3251 } else if (((instr->opc2Value() == 0x4) || (instr->opc2Value() == 0x5)) && |
|
3252 (instr->opc3Value() & 0x1)) { |
|
3253 decodeVCMP(instr); |
|
3254 } else if (((instr->opc2Value() == 0x1)) && (instr->opc3Value() == 0x3)) { |
|
3255 // vsqrt |
|
3256 if (instr->szValue() == 0x1) { |
|
3257 double dm_value = get_double_from_d_register(vm); |
|
3258 double dd_value = std::sqrt(dm_value); |
|
3259 dd_value = canonicalizeNaN(dd_value); |
|
3260 set_d_register_from_double(vd, dd_value); |
|
3261 } else { |
|
3262 float fm_value = get_float_from_s_register(vm); |
|
3263 float fd_value = std::sqrt(fm_value); |
|
3264 fd_value = canonicalizeNaN(fd_value); |
|
3265 set_s_register_from_float(vd, fd_value); |
|
3266 } |
|
3267 } else if (instr->opc3Value() == 0x0) { |
|
3268 // vmov immediate. |
|
3269 if (instr->szValue() == 0x1) { |
|
3270 set_d_register_from_double(vd, instr->doubleImmedVmov()); |
|
3271 } else { |
|
3272 // vmov.f32 immediate |
|
3273 set_s_register_from_float(vd, instr->float32ImmedVmov()); |
|
3274 } |
|
3275 } else { |
|
3276 decodeVCVTBetweenFloatingPointAndIntegerFrac(instr); |
|
3277 } |
|
3278 } else if (instr->opc1Value() == 0x3) { |
|
3279 if (instr->szValue() != 0x1) { |
|
3280 if (instr->opc3Value() & 0x1) { |
|
3281 // vsub |
|
3282 float fn_value = get_float_from_s_register(vn); |
|
3283 float fm_value = get_float_from_s_register(vm); |
|
3284 float fd_value = fn_value - fm_value; |
|
3285 fd_value = canonicalizeNaN(fd_value); |
|
3286 set_s_register_from_float(vd, fd_value); |
|
3287 } else { |
|
3288 // vadd |
|
3289 float fn_value = get_float_from_s_register(vn); |
|
3290 float fm_value = get_float_from_s_register(vm); |
|
3291 float fd_value = fn_value + fm_value; |
|
3292 fd_value = canonicalizeNaN(fd_value); |
|
3293 set_s_register_from_float(vd, fd_value); |
|
3294 } |
|
3295 } else { |
|
3296 if (instr->opc3Value() & 0x1) { |
|
3297 // vsub |
|
3298 double dn_value = get_double_from_d_register(vn); |
|
3299 double dm_value = get_double_from_d_register(vm); |
|
3300 double dd_value = dn_value - dm_value; |
|
3301 dd_value = canonicalizeNaN(dd_value); |
|
3302 set_d_register_from_double(vd, dd_value); |
|
3303 } else { |
|
3304 // vadd |
|
3305 double dn_value = get_double_from_d_register(vn); |
|
3306 double dm_value = get_double_from_d_register(vm); |
|
3307 double dd_value = dn_value + dm_value; |
|
3308 dd_value = canonicalizeNaN(dd_value); |
|
3309 set_d_register_from_double(vd, dd_value); |
|
3310 } |
|
3311 } |
|
3312 } else if ((instr->opc1Value() == 0x2) && !(instr->opc3Value() & 0x1)) { |
|
3313 // vmul |
|
3314 if (instr->szValue() != 0x1) { |
|
3315 float fn_value = get_float_from_s_register(vn); |
|
3316 float fm_value = get_float_from_s_register(vm); |
|
3317 float fd_value = fn_value * fm_value; |
|
3318 fd_value = canonicalizeNaN(fd_value); |
|
3319 set_s_register_from_float(vd, fd_value); |
|
3320 } else { |
|
3321 double dn_value = get_double_from_d_register(vn); |
|
3322 double dm_value = get_double_from_d_register(vm); |
|
3323 double dd_value = dn_value * dm_value; |
|
3324 dd_value = canonicalizeNaN(dd_value); |
|
3325 set_d_register_from_double(vd, dd_value); |
|
3326 } |
|
3327 } else if ((instr->opc1Value() == 0x0)) { |
|
3328 // vmla, vmls |
|
3329 const bool is_vmls = (instr->opc3Value() & 0x1); |
|
3330 |
|
3331 if (instr->szValue() != 0x1) |
|
3332 MOZ_ASSUME_UNREACHABLE(); // Not used by V8. |
|
3333 |
|
3334 const double dd_val = get_double_from_d_register(vd); |
|
3335 const double dn_val = get_double_from_d_register(vn); |
|
3336 const double dm_val = get_double_from_d_register(vm); |
|
3337 |
|
3338 // Note: we do the mul and add/sub in separate steps to avoid getting a |
|
3339 // result with too high precision. |
|
3340 set_d_register_from_double(vd, dn_val * dm_val); |
|
3341 if (is_vmls) { |
|
3342 set_d_register_from_double(vd, |
|
3343 canonicalizeNaN(dd_val - get_double_from_d_register(vd))); |
|
3344 } else { |
|
3345 set_d_register_from_double(vd, |
|
3346 canonicalizeNaN(dd_val + get_double_from_d_register(vd))); |
|
3347 } |
|
3348 } else if ((instr->opc1Value() == 0x4) && !(instr->opc3Value() & 0x1)) { |
|
3349 // vdiv |
|
3350 if (instr->szValue() != 0x1) { |
|
3351 float fn_value = get_float_from_s_register(vn); |
|
3352 float fm_value = get_float_from_s_register(vm); |
|
3353 float fd_value = fn_value / fm_value; |
|
3354 div_zero_vfp_flag_ = (fm_value == 0); |
|
3355 fd_value = canonicalizeNaN(fd_value); |
|
3356 set_s_register_from_float(vd, fd_value); |
|
3357 } else { |
|
3358 double dn_value = get_double_from_d_register(vn); |
|
3359 double dm_value = get_double_from_d_register(vm); |
|
3360 double dd_value = dn_value / dm_value; |
|
3361 div_zero_vfp_flag_ = (dm_value == 0); |
|
3362 dd_value = canonicalizeNaN(dd_value); |
|
3363 set_d_register_from_double(vd, dd_value); |
|
3364 } |
|
3365 } else { |
|
3366 MOZ_CRASH(); |
|
3367 } |
|
3368 } else { |
|
3369 if (instr->VCValue() == 0x0 && instr->VAValue() == 0x0) { |
|
3370 decodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); |
|
3371 } else if ((instr->VLValue() == 0x0) && |
|
3372 (instr->VCValue() == 0x1) && |
|
3373 (instr->bit(23) == 0x0)) { |
|
3374 // vmov (ARM core register to scalar) |
|
3375 int vd = instr->bits(19, 16) | (instr->bit(7) << 4); |
|
3376 double dd_value = get_double_from_d_register(vd); |
|
3377 int32_t data[2]; |
|
3378 memcpy(data, &dd_value, 8); |
|
3379 data[instr->bit(21)] = get_register(instr->rtValue()); |
|
3380 memcpy(&dd_value, data, 8); |
|
3381 set_d_register_from_double(vd, dd_value); |
|
3382 } else if ((instr->VLValue() == 0x1) && |
|
3383 (instr->VCValue() == 0x1) && |
|
3384 (instr->bit(23) == 0x0)) { |
|
3385 // vmov (scalar to ARM core register) |
|
3386 int vn = instr->bits(19, 16) | (instr->bit(7) << 4); |
|
3387 double dn_value = get_double_from_d_register(vn); |
|
3388 int32_t data[2]; |
|
3389 memcpy(data, &dn_value, 8); |
|
3390 set_register(instr->rtValue(), data[instr->bit(21)]); |
|
3391 } else if ((instr->VLValue() == 0x1) && |
|
3392 (instr->VCValue() == 0x0) && |
|
3393 (instr->VAValue() == 0x7) && |
|
3394 (instr->bits(19, 16) == 0x1)) { |
|
3395 // vmrs |
|
3396 uint32_t rt = instr->rtValue(); |
|
3397 if (rt == 0xF) { |
|
3398 copy_FPSCR_to_APSR(); |
|
3399 } else { |
|
3400 // Emulate FPSCR from the Simulator flags. |
|
3401 uint32_t fpscr = (n_flag_FPSCR_ << 31) | |
|
3402 (z_flag_FPSCR_ << 30) | |
|
3403 (c_flag_FPSCR_ << 29) | |
|
3404 (v_flag_FPSCR_ << 28) | |
|
3405 (FPSCR_default_NaN_mode_ << 25) | |
|
3406 (inexact_vfp_flag_ << 4) | |
|
3407 (underflow_vfp_flag_ << 3) | |
|
3408 (overflow_vfp_flag_ << 2) | |
|
3409 (div_zero_vfp_flag_ << 1) | |
|
3410 (inv_op_vfp_flag_ << 0) | |
|
3411 (FPSCR_rounding_mode_); |
|
3412 set_register(rt, fpscr); |
|
3413 } |
|
3414 } else if ((instr->VLValue() == 0x0) && |
|
3415 (instr->VCValue() == 0x0) && |
|
3416 (instr->VAValue() == 0x7) && |
|
3417 (instr->bits(19, 16) == 0x1)) { |
|
3418 // vmsr |
|
3419 uint32_t rt = instr->rtValue(); |
|
3420 if (rt == pc) { |
|
3421 MOZ_CRASH(); |
|
3422 } else { |
|
3423 uint32_t rt_value = get_register(rt); |
|
3424 n_flag_FPSCR_ = (rt_value >> 31) & 1; |
|
3425 z_flag_FPSCR_ = (rt_value >> 30) & 1; |
|
3426 c_flag_FPSCR_ = (rt_value >> 29) & 1; |
|
3427 v_flag_FPSCR_ = (rt_value >> 28) & 1; |
|
3428 FPSCR_default_NaN_mode_ = (rt_value >> 25) & 1; |
|
3429 inexact_vfp_flag_ = (rt_value >> 4) & 1; |
|
3430 underflow_vfp_flag_ = (rt_value >> 3) & 1; |
|
3431 overflow_vfp_flag_ = (rt_value >> 2) & 1; |
|
3432 div_zero_vfp_flag_ = (rt_value >> 1) & 1; |
|
3433 inv_op_vfp_flag_ = (rt_value >> 0) & 1; |
|
3434 FPSCR_rounding_mode_ = |
|
3435 static_cast<VFPRoundingMode>((rt_value) & kVFPRoundingModeMask); |
|
3436 } |
|
3437 } else { |
|
3438 MOZ_CRASH(); |
|
3439 } |
|
3440 } |
|
3441 } |
|
3442 |
|
3443 void |
|
3444 Simulator::decodeVMOVBetweenCoreAndSinglePrecisionRegisters(SimInstruction *instr) |
|
3445 { |
|
3446 MOZ_ASSERT(instr->bit(4) == 1 && |
|
3447 instr->VCValue() == 0x0 && |
|
3448 instr->VAValue() == 0x0); |
|
3449 |
|
3450 int t = instr->rtValue(); |
|
3451 int n = instr->VFPNRegValue(kSinglePrecision); |
|
3452 bool to_arm_register = (instr->VLValue() == 0x1); |
|
3453 if (to_arm_register) { |
|
3454 int32_t int_value = get_sinteger_from_s_register(n); |
|
3455 set_register(t, int_value); |
|
3456 } else { |
|
3457 int32_t rs_val = get_register(t); |
|
3458 set_s_register_from_sinteger(n, rs_val); |
|
3459 } |
|
3460 } |
|
3461 |
|
3462 void |
|
3463 Simulator::decodeVCMP(SimInstruction *instr) |
|
3464 { |
|
3465 MOZ_ASSERT((instr->bit(4) == 0) && (instr->opc1Value() == 0x7)); |
|
3466 MOZ_ASSERT(((instr->opc2Value() == 0x4) || (instr->opc2Value() == 0x5)) && |
|
3467 (instr->opc3Value() & 0x1)); |
|
3468 // Comparison. |
|
3469 |
|
3470 VFPRegPrecision precision = kSinglePrecision; |
|
3471 if (instr->szValue() == 1) |
|
3472 precision = kDoublePrecision; |
|
3473 |
|
3474 int d = instr->VFPDRegValue(precision); |
|
3475 int m = 0; |
|
3476 if (instr->opc2Value() == 0x4) |
|
3477 m = instr->VFPMRegValue(precision); |
|
3478 |
|
3479 if (precision == kDoublePrecision) { |
|
3480 double dd_value = get_double_from_d_register(d); |
|
3481 double dm_value = 0.0; |
|
3482 if (instr->opc2Value() == 0x4) { |
|
3483 dm_value = get_double_from_d_register(m); |
|
3484 } |
|
3485 |
|
3486 // Raise exceptions for quiet NaNs if necessary. |
|
3487 if (instr->bit(7) == 1) { |
|
3488 if (mozilla::IsNaN(dd_value)) |
|
3489 inv_op_vfp_flag_ = true; |
|
3490 } |
|
3491 compute_FPSCR_Flags(dd_value, dm_value); |
|
3492 } else { |
|
3493 float fd_value = get_float_from_s_register(d); |
|
3494 float fm_value = 0.0; |
|
3495 if (instr->opc2Value() == 0x4) |
|
3496 fm_value = get_float_from_s_register(m); |
|
3497 |
|
3498 // Raise exceptions for quiet NaNs if necessary. |
|
3499 if (instr->bit(7) == 1) { |
|
3500 if (mozilla::IsNaN(fd_value)) |
|
3501 inv_op_vfp_flag_ = true; |
|
3502 } |
|
3503 compute_FPSCR_Flags(fd_value, fm_value); |
|
3504 } |
|
3505 } |
|
3506 |
|
3507 void |
|
3508 Simulator::decodeVCVTBetweenDoubleAndSingle(SimInstruction *instr) |
|
3509 { |
|
3510 MOZ_ASSERT(instr->bit(4) == 0 && instr->opc1Value() == 0x7); |
|
3511 MOZ_ASSERT(instr->opc2Value() == 0x7 && instr->opc3Value() == 0x3); |
|
3512 |
|
3513 VFPRegPrecision dst_precision = kDoublePrecision; |
|
3514 VFPRegPrecision src_precision = kSinglePrecision; |
|
3515 if (instr->szValue() == 1) { |
|
3516 dst_precision = kSinglePrecision; |
|
3517 src_precision = kDoublePrecision; |
|
3518 } |
|
3519 |
|
3520 int dst = instr->VFPDRegValue(dst_precision); |
|
3521 int src = instr->VFPMRegValue(src_precision); |
|
3522 |
|
3523 if (dst_precision == kSinglePrecision) { |
|
3524 double val = get_double_from_d_register(src); |
|
3525 set_s_register_from_float(dst, static_cast<float>(val)); |
|
3526 } else { |
|
3527 float val = get_float_from_s_register(src); |
|
3528 set_d_register_from_double(dst, static_cast<double>(val)); |
|
3529 } |
|
3530 } |
|
3531 |
|
3532 static bool |
|
3533 get_inv_op_vfp_flag(VFPRoundingMode mode, double val, bool unsigned_) |
|
3534 { |
|
3535 MOZ_ASSERT(mode == SimRN || mode == SimRM || mode == SimRZ); |
|
3536 double max_uint = static_cast<double>(0xffffffffu); |
|
3537 double max_int = static_cast<double>(INT32_MAX); |
|
3538 double min_int = static_cast<double>(INT32_MIN); |
|
3539 |
|
3540 // Check for NaN. |
|
3541 if (val != val) |
|
3542 return true; |
|
3543 |
|
3544 // Check for overflow. This code works because 32bit integers can be |
|
3545 // exactly represented by ieee-754 64bit floating-point values. |
|
3546 switch (mode) { |
|
3547 case SimRN: |
|
3548 return unsigned_ ? (val >= (max_uint + 0.5)) || |
|
3549 (val < -0.5) |
|
3550 : (val >= (max_int + 0.5)) || |
|
3551 (val < (min_int - 0.5)); |
|
3552 case SimRM: |
|
3553 return unsigned_ ? (val >= (max_uint + 1.0)) || |
|
3554 (val < 0) |
|
3555 : (val >= (max_int + 1.0)) || |
|
3556 (val < min_int); |
|
3557 case SimRZ: |
|
3558 return unsigned_ ? (val >= (max_uint + 1.0)) || |
|
3559 (val <= -1) |
|
3560 : (val >= (max_int + 1.0)) || |
|
3561 (val <= (min_int - 1.0)); |
|
3562 default: |
|
3563 MOZ_CRASH(); |
|
3564 return true; |
|
3565 } |
|
3566 } |
|
3567 |
|
3568 // We call this function only if we had a vfp invalid exception. |
|
3569 // It returns the correct saturated value. |
|
3570 static int |
|
3571 VFPConversionSaturate(double val, bool unsigned_res) |
|
3572 { |
|
3573 if (val != val) // NaN. |
|
3574 return 0; |
|
3575 if (unsigned_res) |
|
3576 return (val < 0) ? 0 : 0xffffffffu; |
|
3577 return (val < 0) ? INT32_MIN : INT32_MAX; |
|
3578 } |
|
3579 |
|
3580 void |
|
3581 Simulator::decodeVCVTBetweenFloatingPointAndInteger(SimInstruction *instr) |
|
3582 { |
|
3583 MOZ_ASSERT((instr->bit(4) == 0) && (instr->opc1Value() == 0x7) && |
|
3584 (instr->bits(27, 23) == 0x1D)); |
|
3585 MOZ_ASSERT(((instr->opc2Value() == 0x8) && (instr->opc3Value() & 0x1)) || |
|
3586 (((instr->opc2Value() >> 1) == 0x6) && (instr->opc3Value() & 0x1))); |
|
3587 |
|
3588 // Conversion between floating-point and integer. |
|
3589 bool to_integer = (instr->bit(18) == 1); |
|
3590 |
|
3591 VFPRegPrecision src_precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; |
|
3592 |
|
3593 if (to_integer) { |
|
3594 // We are playing with code close to the C++ standard's limits below, |
|
3595 // hence the very simple code and heavy checks. |
|
3596 // |
|
3597 // Note: |
|
3598 // C++ defines default type casting from floating point to integer as |
|
3599 // (close to) rounding toward zero ("fractional part discarded"). |
|
3600 |
|
3601 int dst = instr->VFPDRegValue(kSinglePrecision); |
|
3602 int src = instr->VFPMRegValue(src_precision); |
|
3603 |
|
3604 // Bit 7 in vcvt instructions indicates if we should use the FPSCR rounding |
|
3605 // mode or the default Round to Zero mode. |
|
3606 VFPRoundingMode mode = (instr->bit(7) != 1) ? FPSCR_rounding_mode_ : SimRZ; |
|
3607 MOZ_ASSERT(mode == SimRM || mode == SimRZ || mode == SimRN); |
|
3608 |
|
3609 bool unsigned_integer = (instr->bit(16) == 0); |
|
3610 bool double_precision = (src_precision == kDoublePrecision); |
|
3611 |
|
3612 double val = double_precision |
|
3613 ? get_double_from_d_register(src) |
|
3614 : get_float_from_s_register(src); |
|
3615 |
|
3616 int temp = unsigned_integer ? static_cast<uint32_t>(val) : static_cast<int32_t>(val); |
|
3617 |
|
3618 inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); |
|
3619 |
|
3620 double abs_diff = unsigned_integer |
|
3621 ? std::fabs(val - static_cast<uint32_t>(temp)) |
|
3622 : std::fabs(val - temp); |
|
3623 |
|
3624 inexact_vfp_flag_ = (abs_diff != 0); |
|
3625 |
|
3626 if (inv_op_vfp_flag_) { |
|
3627 temp = VFPConversionSaturate(val, unsigned_integer); |
|
3628 } else { |
|
3629 switch (mode) { |
|
3630 case SimRN: { |
|
3631 int val_sign = (val > 0) ? 1 : -1; |
|
3632 if (abs_diff > 0.5) { |
|
3633 temp += val_sign; |
|
3634 } else if (abs_diff == 0.5) { |
|
3635 // Round to even if exactly halfway. |
|
3636 temp = ((temp % 2) == 0) ? temp : temp + val_sign; |
|
3637 } |
|
3638 break; |
|
3639 } |
|
3640 |
|
3641 case SimRM: |
|
3642 temp = temp > val ? temp - 1 : temp; |
|
3643 break; |
|
3644 |
|
3645 case SimRZ: |
|
3646 // Nothing to do. |
|
3647 break; |
|
3648 |
|
3649 default: |
|
3650 MOZ_CRASH(); |
|
3651 } |
|
3652 } |
|
3653 |
|
3654 // Update the destination register. |
|
3655 set_s_register_from_sinteger(dst, temp); |
|
3656 } else { |
|
3657 bool unsigned_integer = (instr->bit(7) == 0); |
|
3658 int dst = instr->VFPDRegValue(src_precision); |
|
3659 int src = instr->VFPMRegValue(kSinglePrecision); |
|
3660 |
|
3661 int val = get_sinteger_from_s_register(src); |
|
3662 |
|
3663 if (src_precision == kDoublePrecision) { |
|
3664 if (unsigned_integer) |
|
3665 set_d_register_from_double(dst, static_cast<double>(static_cast<uint32_t>(val))); |
|
3666 else |
|
3667 set_d_register_from_double(dst, static_cast<double>(val)); |
|
3668 } else { |
|
3669 if (unsigned_integer) |
|
3670 set_s_register_from_float(dst, static_cast<float>(static_cast<uint32_t>(val))); |
|
3671 else |
|
3672 set_s_register_from_float(dst, static_cast<float>(val)); |
|
3673 } |
|
3674 } |
|
3675 } |
|
3676 |
|
3677 // A VFPv3 specific instruction. |
|
3678 void |
|
3679 Simulator::decodeVCVTBetweenFloatingPointAndIntegerFrac(SimInstruction *instr) |
|
3680 { |
|
3681 MOZ_ASSERT(instr->bits(27, 24) == 0xE && instr->opc1Value() == 0x7 && instr->bit(19) == 1 && |
|
3682 instr->bit(17) == 1 && instr->bits(11,9) == 0x5 && instr->bit(6) == 1 && |
|
3683 instr->bit(4) == 0); |
|
3684 |
|
3685 int size = (instr->bit(7) == 1) ? 32 : 16; |
|
3686 |
|
3687 int fraction_bits = size - ((instr->bits(3, 0) << 1) | instr->bit(5)); |
|
3688 double mult = 1 << fraction_bits; |
|
3689 |
|
3690 MOZ_ASSERT(size == 32); // Only handling size == 32 for now. |
|
3691 |
|
3692 // Conversion between floating-point and integer. |
|
3693 bool to_fixed = (instr->bit(18) == 1); |
|
3694 |
|
3695 VFPRegPrecision precision = (instr->szValue() == 1) ? kDoublePrecision : kSinglePrecision; |
|
3696 |
|
3697 if (to_fixed) { |
|
3698 // We are playing with code close to the C++ standard's limits below, |
|
3699 // hence the very simple code and heavy checks. |
|
3700 // |
|
3701 // Note: C++ defines default type casting from floating point to integer as |
|
3702 // (close to) rounding toward zero ("fractional part discarded"). |
|
3703 |
|
3704 int dst = instr->VFPDRegValue(precision); |
|
3705 |
|
3706 bool unsigned_integer = (instr->bit(16) == 1); |
|
3707 bool double_precision = (precision == kDoublePrecision); |
|
3708 |
|
3709 double val = double_precision |
|
3710 ? get_double_from_d_register(dst) |
|
3711 : get_float_from_s_register(dst); |
|
3712 |
|
3713 // Scale value by specified number of fraction bits. |
|
3714 val *= mult; |
|
3715 |
|
3716 // Rounding down towards zero. No need to account for the rounding error as this |
|
3717 // instruction always rounds down towards zero. See SimRZ below. |
|
3718 int temp = unsigned_integer ? static_cast<uint32_t>(val) : static_cast<int32_t>(val); |
|
3719 |
|
3720 inv_op_vfp_flag_ = get_inv_op_vfp_flag(SimRZ, val, unsigned_integer); |
|
3721 |
|
3722 double abs_diff = unsigned_integer |
|
3723 ? std::fabs(val - static_cast<uint32_t>(temp)) |
|
3724 : std::fabs(val - temp); |
|
3725 |
|
3726 inexact_vfp_flag_ = (abs_diff != 0); |
|
3727 |
|
3728 if (inv_op_vfp_flag_) |
|
3729 temp = VFPConversionSaturate(val, unsigned_integer); |
|
3730 |
|
3731 // Update the destination register. |
|
3732 if (double_precision) { |
|
3733 uint32_t dbl[2]; |
|
3734 dbl[0] = temp; dbl[1] = 0; |
|
3735 set_d_register(dst, dbl); |
|
3736 } else { |
|
3737 set_s_register_from_sinteger(dst, temp); |
|
3738 } |
|
3739 } else { |
|
3740 MOZ_ASSUME_UNREACHABLE(); // Not implemented, fixed to float. |
|
3741 } |
|
3742 } |
|
3743 |
|
3744 void |
|
3745 Simulator::decodeType6CoprocessorIns(SimInstruction *instr) |
|
3746 { |
|
3747 MOZ_ASSERT(instr->typeValue() == 6); |
|
3748 |
|
3749 if (instr->coprocessorValue() == 0xA) { |
|
3750 switch (instr->opcodeValue()) { |
|
3751 case 0x8: |
|
3752 case 0xA: |
|
3753 case 0xC: |
|
3754 case 0xE: { // Load and store single precision float to memory. |
|
3755 int rn = instr->rnValue(); |
|
3756 int vd = instr->VFPDRegValue(kSinglePrecision); |
|
3757 int offset = instr->immed8Value(); |
|
3758 if (!instr->hasU()) |
|
3759 offset = -offset; |
|
3760 |
|
3761 int32_t address = get_register(rn) + 4 * offset; |
|
3762 if (instr->hasL()) { |
|
3763 // Load double from memory: vldr. |
|
3764 set_s_register_from_sinteger(vd, readW(address, instr)); |
|
3765 } else { |
|
3766 // Store double to memory: vstr. |
|
3767 writeW(address, get_sinteger_from_s_register(vd), instr); |
|
3768 } |
|
3769 break; |
|
3770 } |
|
3771 case 0x4: |
|
3772 case 0x5: |
|
3773 case 0x6: |
|
3774 case 0x7: |
|
3775 case 0x9: |
|
3776 case 0xB: |
|
3777 // Load/store multiple single from memory: vldm/vstm. |
|
3778 handleVList(instr); |
|
3779 break; |
|
3780 default: |
|
3781 MOZ_CRASH(); |
|
3782 } |
|
3783 } else if (instr->coprocessorValue() == 0xB) { |
|
3784 switch (instr->opcodeValue()) { |
|
3785 case 0x2: |
|
3786 // Load and store double to two GP registers |
|
3787 if (instr->bits(7, 6) != 0 || instr->bit(4) != 1) { |
|
3788 MOZ_CRASH(); // Not used atm. |
|
3789 } else { |
|
3790 int rt = instr->rtValue(); |
|
3791 int rn = instr->rnValue(); |
|
3792 int vm = instr->VFPMRegValue(kDoublePrecision); |
|
3793 if (instr->hasL()) { |
|
3794 int32_t data[2]; |
|
3795 double d = get_double_from_d_register(vm); |
|
3796 memcpy(data, &d, 8); |
|
3797 set_register(rt, data[0]); |
|
3798 set_register(rn, data[1]); |
|
3799 } else { |
|
3800 int32_t data[] = { get_register(rt), get_register(rn) }; |
|
3801 double d; |
|
3802 memcpy(&d, data, 8); |
|
3803 set_d_register_from_double(vm, d); |
|
3804 } |
|
3805 } |
|
3806 break; |
|
3807 case 0x8: |
|
3808 case 0xA: |
|
3809 case 0xC: |
|
3810 case 0xE: { // Load and store double to memory. |
|
3811 int rn = instr->rnValue(); |
|
3812 int vd = instr->VFPDRegValue(kDoublePrecision); |
|
3813 int offset = instr->immed8Value(); |
|
3814 if (!instr->hasU()) |
|
3815 offset = -offset; |
|
3816 int32_t address = get_register(rn) + 4 * offset; |
|
3817 if (instr->hasL()) { |
|
3818 // Load double from memory: vldr. |
|
3819 int32_t data[] = { |
|
3820 readW(address, instr), |
|
3821 readW(address + 4, instr) |
|
3822 }; |
|
3823 double val; |
|
3824 memcpy(&val, data, 8); |
|
3825 set_d_register_from_double(vd, val); |
|
3826 } else { |
|
3827 // Store double to memory: vstr. |
|
3828 int32_t data[2]; |
|
3829 double val = get_double_from_d_register(vd); |
|
3830 memcpy(data, &val, 8); |
|
3831 writeW(address, data[0], instr); |
|
3832 writeW(address + 4, data[1], instr); |
|
3833 } |
|
3834 break; |
|
3835 } |
|
3836 case 0x4: |
|
3837 case 0x5: |
|
3838 case 0x6: |
|
3839 case 0x7: |
|
3840 case 0x9: |
|
3841 case 0xB: |
|
3842 // Load/store multiple double from memory: vldm/vstm. |
|
3843 handleVList(instr); |
|
3844 break; |
|
3845 default: |
|
3846 MOZ_CRASH(); |
|
3847 } |
|
3848 } else { |
|
3849 MOZ_CRASH(); |
|
3850 } |
|
3851 } |
|
3852 |
|
3853 void |
|
3854 Simulator::decodeSpecialCondition(SimInstruction *instr) |
|
3855 { |
|
3856 switch (instr->specialValue()) { |
|
3857 case 5: |
|
3858 if (instr->bits(18, 16) == 0 && instr->bits(11, 6) == 0x28 && instr->bit(4) == 1) { |
|
3859 // vmovl signed |
|
3860 int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
|
3861 int Vm = (instr->bit(5) << 4) | instr->vmValue(); |
|
3862 int imm3 = instr->bits(21, 19); |
|
3863 if (imm3 != 1 && imm3 != 2 && imm3 != 4) |
|
3864 MOZ_CRASH(); |
|
3865 int esize = 8 * imm3; |
|
3866 int elements = 64 / esize; |
|
3867 int8_t from[8]; |
|
3868 get_d_register(Vm, reinterpret_cast<uint64_t*>(from)); |
|
3869 int16_t to[8]; |
|
3870 int e = 0; |
|
3871 while (e < elements) { |
|
3872 to[e] = from[e]; |
|
3873 e++; |
|
3874 } |
|
3875 set_q_register(Vd, reinterpret_cast<uint64_t*>(to)); |
|
3876 } else { |
|
3877 MOZ_CRASH(); |
|
3878 } |
|
3879 break; |
|
3880 case 7: |
|
3881 if (instr->bits(18, 16) == 0 && instr->bits(11, 6) == 0x28 && instr->bit(4) == 1) { |
|
3882 // vmovl unsigned |
|
3883 int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
|
3884 int Vm = (instr->bit(5) << 4) | instr->vmValue(); |
|
3885 int imm3 = instr->bits(21, 19); |
|
3886 if (imm3 != 1 && imm3 != 2 && imm3 != 4) |
|
3887 MOZ_CRASH(); |
|
3888 int esize = 8 * imm3; |
|
3889 int elements = 64 / esize; |
|
3890 uint8_t from[8]; |
|
3891 get_d_register(Vm, reinterpret_cast<uint64_t*>(from)); |
|
3892 uint16_t to[8]; |
|
3893 int e = 0; |
|
3894 while (e < elements) { |
|
3895 to[e] = from[e]; |
|
3896 e++; |
|
3897 } |
|
3898 set_q_register(Vd, reinterpret_cast<uint64_t*>(to)); |
|
3899 } else { |
|
3900 MOZ_CRASH(); |
|
3901 } |
|
3902 break; |
|
3903 case 8: |
|
3904 if (instr->bits(21, 20) == 0) { |
|
3905 // vst1 |
|
3906 int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
|
3907 int Rn = instr->vnValue(); |
|
3908 int type = instr->bits(11, 8); |
|
3909 int Rm = instr->vmValue(); |
|
3910 int32_t address = get_register(Rn); |
|
3911 int regs = 0; |
|
3912 switch (type) { |
|
3913 case nlt_1: |
|
3914 regs = 1; |
|
3915 break; |
|
3916 case nlt_2: |
|
3917 regs = 2; |
|
3918 break; |
|
3919 case nlt_3: |
|
3920 regs = 3; |
|
3921 break; |
|
3922 case nlt_4: |
|
3923 regs = 4; |
|
3924 break; |
|
3925 default: |
|
3926 MOZ_CRASH(); |
|
3927 break; |
|
3928 } |
|
3929 int r = 0; |
|
3930 while (r < regs) { |
|
3931 uint32_t data[2]; |
|
3932 get_d_register(Vd + r, data); |
|
3933 writeW(address, data[0], instr); |
|
3934 writeW(address + 4, data[1], instr); |
|
3935 address += 8; |
|
3936 r++; |
|
3937 } |
|
3938 if (Rm != 15) { |
|
3939 if (Rm == 13) |
|
3940 set_register(Rn, address); |
|
3941 else |
|
3942 set_register(Rn, get_register(Rn) + get_register(Rm)); |
|
3943 } |
|
3944 } else if (instr->bits(21, 20) == 2) { |
|
3945 // vld1 |
|
3946 int Vd = (instr->bit(22) << 4) | instr->vdValue(); |
|
3947 int Rn = instr->vnValue(); |
|
3948 int type = instr->bits(11, 8); |
|
3949 int Rm = instr->vmValue(); |
|
3950 int32_t address = get_register(Rn); |
|
3951 int regs = 0; |
|
3952 switch (type) { |
|
3953 case nlt_1: |
|
3954 regs = 1; |
|
3955 break; |
|
3956 case nlt_2: |
|
3957 regs = 2; |
|
3958 break; |
|
3959 case nlt_3: |
|
3960 regs = 3; |
|
3961 break; |
|
3962 case nlt_4: |
|
3963 regs = 4; |
|
3964 break; |
|
3965 default: |
|
3966 MOZ_CRASH(); |
|
3967 break; |
|
3968 } |
|
3969 int r = 0; |
|
3970 while (r < regs) { |
|
3971 uint32_t data[2]; |
|
3972 data[0] = readW(address, instr); |
|
3973 data[1] = readW(address + 4, instr); |
|
3974 set_d_register(Vd + r, data); |
|
3975 address += 8; |
|
3976 r++; |
|
3977 } |
|
3978 if (Rm != 15) { |
|
3979 if (Rm == 13) |
|
3980 set_register(Rn, address); |
|
3981 else |
|
3982 set_register(Rn, get_register(Rn) + get_register(Rm)); |
|
3983 } |
|
3984 } else { |
|
3985 MOZ_CRASH(); |
|
3986 } |
|
3987 break; |
|
3988 case 0xA: |
|
3989 case 0xB: |
|
3990 if (instr->bits(22, 20) == 5 && instr->bits(15, 12) == 0xf) { |
|
3991 // pld: ignore instruction. |
|
3992 } else { |
|
3993 MOZ_CRASH(); |
|
3994 } |
|
3995 break; |
|
3996 default: |
|
3997 MOZ_CRASH(); |
|
3998 } |
|
3999 } |
|
4000 |
|
4001 // Executes the current instruction. |
|
4002 void |
|
4003 Simulator::instructionDecode(SimInstruction *instr) |
|
4004 { |
|
4005 if (Simulator::ICacheCheckingEnabled) { |
|
4006 AutoLockSimulatorRuntime alsr(srt_); |
|
4007 CheckICache(srt_->icache(), instr); |
|
4008 } |
|
4009 |
|
4010 pc_modified_ = false; |
|
4011 |
|
4012 static const uint32_t kSpecialCondition = 15 << 28; |
|
4013 if (instr->conditionField() == kSpecialCondition) { |
|
4014 decodeSpecialCondition(instr); |
|
4015 } else if (conditionallyExecute(instr)) { |
|
4016 switch (instr->typeValue()) { |
|
4017 case 0: |
|
4018 case 1: |
|
4019 decodeType01(instr); |
|
4020 break; |
|
4021 case 2: |
|
4022 decodeType2(instr); |
|
4023 break; |
|
4024 case 3: |
|
4025 decodeType3(instr); |
|
4026 break; |
|
4027 case 4: |
|
4028 decodeType4(instr); |
|
4029 break; |
|
4030 case 5: |
|
4031 decodeType5(instr); |
|
4032 break; |
|
4033 case 6: |
|
4034 decodeType6(instr); |
|
4035 break; |
|
4036 case 7: |
|
4037 decodeType7(instr); |
|
4038 break; |
|
4039 default: |
|
4040 MOZ_CRASH(); |
|
4041 break; |
|
4042 } |
|
4043 // If the instruction is a non taken conditional stop, we need to skip the |
|
4044 // inlined message address. |
|
4045 } else if (instr->isStop()) { |
|
4046 set_pc(get_pc() + 2 * SimInstruction::kInstrSize); |
|
4047 } |
|
4048 if (!pc_modified_) |
|
4049 set_register(pc, reinterpret_cast<int32_t>(instr) + SimInstruction::kInstrSize); |
|
4050 } |
|
4051 |
|
4052 |
|
4053 template<bool EnableStopSimAt> |
|
4054 void |
|
4055 Simulator::execute() |
|
4056 { |
|
4057 // Get the PC to simulate. Cannot use the accessor here as we need the |
|
4058 // raw PC value and not the one used as input to arithmetic instructions. |
|
4059 int program_counter = get_pc(); |
|
4060 AsmJSActivation *activation = TlsPerThreadData.get()->asmJSActivationStackFromOwnerThread(); |
|
4061 |
|
4062 while (program_counter != end_sim_pc) { |
|
4063 if (EnableStopSimAt && (icount_ == Simulator::StopSimAt)) { |
|
4064 fprintf(stderr, "\nStopped simulation at icount %lld\n", icount_); |
|
4065 ArmDebugger dbg(this); |
|
4066 dbg.debug(); |
|
4067 } else { |
|
4068 SimInstruction *instr = reinterpret_cast<SimInstruction *>(program_counter); |
|
4069 instructionDecode(instr); |
|
4070 icount_++; |
|
4071 |
|
4072 int32_t rpc = resume_pc_; |
|
4073 if (MOZ_UNLIKELY(rpc != 0)) { |
|
4074 // AsmJS signal handler ran and we have to adjust the pc. |
|
4075 activation->setInterrupted((void *)get_pc()); |
|
4076 set_pc(rpc); |
|
4077 resume_pc_ = 0; |
|
4078 } |
|
4079 } |
|
4080 program_counter = get_pc(); |
|
4081 } |
|
4082 } |
|
4083 |
|
4084 void |
|
4085 Simulator::callInternal(uint8_t *entry) |
|
4086 { |
|
4087 // Prepare to execute the code at entry. |
|
4088 set_register(pc, reinterpret_cast<int32_t>(entry)); |
|
4089 |
|
4090 // Put down marker for end of simulation. The simulator will stop simulation |
|
4091 // when the PC reaches this value. By saving the "end simulation" value into |
|
4092 // the LR the simulation stops when returning to this call point. |
|
4093 set_register(lr, end_sim_pc); |
|
4094 |
|
4095 // Remember the values of callee-saved registers. |
|
4096 // The code below assumes that r9 is not used as sb (static base) in |
|
4097 // simulator code and therefore is regarded as a callee-saved register. |
|
4098 int32_t r4_val = get_register(r4); |
|
4099 int32_t r5_val = get_register(r5); |
|
4100 int32_t r6_val = get_register(r6); |
|
4101 int32_t r7_val = get_register(r7); |
|
4102 int32_t r8_val = get_register(r8); |
|
4103 int32_t r9_val = get_register(r9); |
|
4104 int32_t r10_val = get_register(r10); |
|
4105 int32_t r11_val = get_register(r11); |
|
4106 |
|
4107 // Remember d8 to d15 which are callee-saved. |
|
4108 uint64_t d8_val; |
|
4109 get_d_register(d8, &d8_val); |
|
4110 uint64_t d9_val; |
|
4111 get_d_register(d9, &d9_val); |
|
4112 uint64_t d10_val; |
|
4113 get_d_register(d10, &d10_val); |
|
4114 uint64_t d11_val; |
|
4115 get_d_register(d11, &d11_val); |
|
4116 uint64_t d12_val; |
|
4117 get_d_register(d12, &d12_val); |
|
4118 uint64_t d13_val; |
|
4119 get_d_register(d13, &d13_val); |
|
4120 uint64_t d14_val; |
|
4121 get_d_register(d14, &d14_val); |
|
4122 uint64_t d15_val; |
|
4123 get_d_register(d15, &d15_val); |
|
4124 |
|
4125 // Set up the callee-saved registers with a known value. To be able to check |
|
4126 // that they are preserved properly across JS execution. |
|
4127 int32_t callee_saved_value = uint32_t(icount_); |
|
4128 set_register(r4, callee_saved_value); |
|
4129 set_register(r5, callee_saved_value); |
|
4130 set_register(r6, callee_saved_value); |
|
4131 set_register(r7, callee_saved_value); |
|
4132 set_register(r8, callee_saved_value); |
|
4133 set_register(r9, callee_saved_value); |
|
4134 set_register(r10, callee_saved_value); |
|
4135 set_register(r11, callee_saved_value); |
|
4136 |
|
4137 uint64_t callee_saved_value_d = uint64_t(icount_); |
|
4138 set_d_register(d8, &callee_saved_value_d); |
|
4139 set_d_register(d9, &callee_saved_value_d); |
|
4140 set_d_register(d10, &callee_saved_value_d); |
|
4141 set_d_register(d11, &callee_saved_value_d); |
|
4142 set_d_register(d12, &callee_saved_value_d); |
|
4143 set_d_register(d13, &callee_saved_value_d); |
|
4144 set_d_register(d14, &callee_saved_value_d); |
|
4145 set_d_register(d15, &callee_saved_value_d); |
|
4146 |
|
4147 // Start the simulation |
|
4148 if (Simulator::StopSimAt != -1L) |
|
4149 execute<true>(); |
|
4150 else |
|
4151 execute<false>(); |
|
4152 |
|
4153 // Check that the callee-saved registers have been preserved. |
|
4154 MOZ_ASSERT(callee_saved_value == get_register(r4)); |
|
4155 MOZ_ASSERT(callee_saved_value == get_register(r5)); |
|
4156 MOZ_ASSERT(callee_saved_value == get_register(r6)); |
|
4157 MOZ_ASSERT(callee_saved_value == get_register(r7)); |
|
4158 MOZ_ASSERT(callee_saved_value == get_register(r8)); |
|
4159 MOZ_ASSERT(callee_saved_value == get_register(r9)); |
|
4160 MOZ_ASSERT(callee_saved_value == get_register(r10)); |
|
4161 MOZ_ASSERT(callee_saved_value == get_register(r11)); |
|
4162 |
|
4163 uint64_t value; |
|
4164 get_d_register(d8, &value); |
|
4165 MOZ_ASSERT(callee_saved_value_d == value); |
|
4166 get_d_register(d9, &value); |
|
4167 MOZ_ASSERT(callee_saved_value_d == value); |
|
4168 get_d_register(d10, &value); |
|
4169 MOZ_ASSERT(callee_saved_value_d == value); |
|
4170 get_d_register(d11, &value); |
|
4171 MOZ_ASSERT(callee_saved_value_d == value); |
|
4172 get_d_register(d12, &value); |
|
4173 MOZ_ASSERT(callee_saved_value_d == value); |
|
4174 get_d_register(d13, &value); |
|
4175 MOZ_ASSERT(callee_saved_value_d == value); |
|
4176 get_d_register(d14, &value); |
|
4177 MOZ_ASSERT(callee_saved_value_d == value); |
|
4178 get_d_register(d15, &value); |
|
4179 MOZ_ASSERT(callee_saved_value_d == value); |
|
4180 |
|
4181 // Restore callee-saved registers with the original value. |
|
4182 set_register(r4, r4_val); |
|
4183 set_register(r5, r5_val); |
|
4184 set_register(r6, r6_val); |
|
4185 set_register(r7, r7_val); |
|
4186 set_register(r8, r8_val); |
|
4187 set_register(r9, r9_val); |
|
4188 set_register(r10, r10_val); |
|
4189 set_register(r11, r11_val); |
|
4190 |
|
4191 set_d_register(d8, &d8_val); |
|
4192 set_d_register(d9, &d9_val); |
|
4193 set_d_register(d10, &d10_val); |
|
4194 set_d_register(d11, &d11_val); |
|
4195 set_d_register(d12, &d12_val); |
|
4196 set_d_register(d13, &d13_val); |
|
4197 set_d_register(d14, &d14_val); |
|
4198 set_d_register(d15, &d15_val); |
|
4199 } |
|
4200 |
|
4201 int64_t |
|
4202 Simulator::call(uint8_t* entry, int argument_count, ...) |
|
4203 { |
|
4204 va_list parameters; |
|
4205 va_start(parameters, argument_count); |
|
4206 |
|
4207 // First four arguments passed in registers. |
|
4208 MOZ_ASSERT(argument_count >= 2); |
|
4209 set_register(r0, va_arg(parameters, int32_t)); |
|
4210 set_register(r1, va_arg(parameters, int32_t)); |
|
4211 if (argument_count >= 3) |
|
4212 set_register(r2, va_arg(parameters, int32_t)); |
|
4213 if (argument_count >= 4) |
|
4214 set_register(r3, va_arg(parameters, int32_t)); |
|
4215 |
|
4216 // Remaining arguments passed on stack. |
|
4217 int original_stack = get_register(sp); |
|
4218 int entry_stack = original_stack; |
|
4219 if (argument_count >= 4) |
|
4220 entry_stack -= (argument_count - 4) * sizeof(int32_t); |
|
4221 |
|
4222 entry_stack &= ~StackAlignment; |
|
4223 |
|
4224 // Store remaining arguments on stack, from low to high memory. |
|
4225 intptr_t *stack_argument = reinterpret_cast<intptr_t*>(entry_stack); |
|
4226 for (int i = 4; i < argument_count; i++) |
|
4227 stack_argument[i - 4] = va_arg(parameters, int32_t); |
|
4228 va_end(parameters); |
|
4229 set_register(sp, entry_stack); |
|
4230 |
|
4231 callInternal(entry); |
|
4232 |
|
4233 // Pop stack passed arguments. |
|
4234 MOZ_ASSERT(entry_stack == get_register(sp)); |
|
4235 set_register(sp, original_stack); |
|
4236 |
|
4237 int64_t result = (int64_t(get_register(r1)) << 32) | get_register(r0); |
|
4238 return result; |
|
4239 } |
|
4240 |
|
4241 Simulator * |
|
4242 Simulator::Current() |
|
4243 { |
|
4244 PerThreadData *pt = TlsPerThreadData.get(); |
|
4245 Simulator *sim = pt->simulator(); |
|
4246 if (!sim) { |
|
4247 sim = js_new<Simulator>(pt->simulatorRuntime()); |
|
4248 pt->setSimulator(sim); |
|
4249 } |
|
4250 |
|
4251 return sim; |
|
4252 } |
|
4253 |
|
4254 } // namespace jit |
|
4255 } // namespace js |
|
4256 |
|
4257 js::jit::Simulator * |
|
4258 js::PerThreadData::simulator() const |
|
4259 { |
|
4260 return simulator_; |
|
4261 } |
|
4262 |
|
4263 void |
|
4264 js::PerThreadData::setSimulator(js::jit::Simulator *sim) |
|
4265 { |
|
4266 simulator_ = sim; |
|
4267 simulatorStackLimit_ = sim->stackLimit(); |
|
4268 } |
|
4269 |
|
4270 js::jit::SimulatorRuntime * |
|
4271 js::PerThreadData::simulatorRuntime() const |
|
4272 { |
|
4273 return runtime_->simulatorRuntime(); |
|
4274 } |
|
4275 |
|
4276 uintptr_t * |
|
4277 js::PerThreadData::addressOfSimulatorStackLimit() |
|
4278 { |
|
4279 return &simulatorStackLimit_; |
|
4280 } |
|
4281 |
|
4282 js::jit::SimulatorRuntime * |
|
4283 JSRuntime::simulatorRuntime() const |
|
4284 { |
|
4285 return simulatorRuntime_; |
|
4286 } |
|
4287 |
|
4288 void |
|
4289 JSRuntime::setSimulatorRuntime(js::jit::SimulatorRuntime *srt) |
|
4290 { |
|
4291 MOZ_ASSERT(!simulatorRuntime_); |
|
4292 simulatorRuntime_ = srt; |
|
4293 } |