Collect worker memory stats
[hiphop-php.git] / hphp / vixl / a64 / simulator-a64.h
blob2a87e4c32746a692a520d175e69ce221490b7994
1 // Copyright 2013, ARM Limited
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright notice,
10 // this list of conditions and the following disclaimer in the documentation
11 // and/or other materials provided with the distribution.
12 // * Neither the name of ARM Limited nor the names of its contributors may be
13 // used to endorse or promote products derived from this software without
14 // specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #ifndef VIXL_A64_SIMULATOR_A64_H_
28 #define VIXL_A64_SIMULATOR_A64_H_
30 #include <iosfwd>
31 #include <stack>
33 #include "hphp/vixl/globals.h"
34 #include "hphp/vixl/utils.h"
35 #include "hphp/vixl/a64/instructions-a64.h"
36 #include "hphp/vixl/a64/assembler-a64.h"
37 #include "hphp/vixl/a64/disasm-a64.h"
38 #include "hphp/vixl/a64/instrument-a64.h"
40 namespace vixl {
42 enum ReverseByteMode {
43 Reverse16 = 0,
44 Reverse32 = 1,
45 Reverse64 = 2
48 // Printf. See debugger-a64.h for more information on pseudo instructions.
49 // - type: CPURegister::RegisterType stored as a uint32_t.
51 // Simulate a call to printf.
53 // Floating-point and integer arguments are passed in separate sets of
54 // registers in AAPCS64 (even for varargs functions), so it is not possible to
55 // determine the type of location of each argument without some information
56 // about the values that were passed in. This information could be retrieved
57 // from the printf format string, but the format string is not trivial to
58 // parse so we encode the relevant information with the HLT instruction under
59 // the type argument. Therefore the interface is:
60 // x0: The format string
61 // x1-x7: Optional arguments, if type == CPURegister::kRegister
62 // d0-d7: Optional arguments, if type == CPURegister::kFPRegister
63 constexpr Instr kPrintfOpcode = 0xdeb1;
64 constexpr unsigned kPrintfTypeOffset = 1 * kInstructionSize;
65 constexpr unsigned kPrintfLength = 2 * kInstructionSize;
67 constexpr Instr kHostCallOpcode = 0xdeb4;
68 constexpr unsigned kHostCallCountOffset = 1 * kInstructionSize;
70 // The proper way to initialize a simulated system register (such as NZCV) is as
71 // follows:
72 // SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV);
73 class SimSystemRegister {
74 public:
75 // The default constructor represents a register which has no writable bits.
76 // It is not possible to set its value to anything other than 0.
77 SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { }
79 inline uint32_t RawValue() const {
80 return value_;
83 inline void SetRawValue(uint32_t new_value) {
84 value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_);
87 inline uint32_t Bits(int msb, int lsb) const {
88 return unsigned_bitextract_32(msb, lsb, value_);
91 inline int32_t SignedBits(int msb, int lsb) const {
92 return signed_bitextract_32(msb, lsb, value_);
95 void SetBits(int msb, int lsb, uint32_t bits);
97 // Default system register values.
98 static SimSystemRegister DefaultValueFor(SystemRegister id);
100 #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \
101 inline uint32_t Name() const { return Func(HighBit, LowBit); } \
102 inline void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); }
103 #define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \
104 static const uint32_t Name##WriteIgnoreMask = ~static_cast<uint32_t>(Mask);
106 SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK)
108 #undef DEFINE_ZERO_BITS
109 #undef DEFINE_GETTER
111 protected:
112 // Most system registers only implement a few of the bits in the word. Other
113 // bits are "read-as-zero, write-ignored". The write_ignore_mask argument
114 // describes the bits which are not modifiable.
115 SimSystemRegister(uint32_t value, uint32_t write_ignore_mask)
116 : value_(value), write_ignore_mask_(write_ignore_mask) { }
118 uint32_t value_;
119 uint32_t write_ignore_mask_;
122 class Simulator : public DecoderVisitor {
123 public:
124 explicit Simulator(Decoder* decoder, std::ostream& stream);
125 ~Simulator() override;
127 void ResetState();
129 // TODO: We assume little endianness, and the way in which the members of this
130 // union overlay. Add tests to ensure this, or fix accessors to no longer
131 // require this assumption.
132 union SimRegister {
133 int64_t x;
134 int32_t w;
137 union SimFPRegister {
138 double d;
139 float s;
142 // When an exception is thrown through simulated code, call this hook. The
143 // hook returns a new instruction pointer at which to resume execution, or
144 // nullptr to continue execution normally.
145 typedef Instruction*(*ExceptionHook)(Simulator*, std::exception_ptr);
146 void set_exception_hook(ExceptionHook hook) {
147 exception_hook_ = hook;
150 void resume_last_exception() {
151 auto exn = exns_in_flight_.top();
152 exns_in_flight_.pop();
153 std::rethrow_exception(exn);
156 // Run the simulator.
157 virtual void Run();
158 void RunFrom(Instruction* first);
160 // Simulation helpers.
161 inline Instruction* pc() const { return pc_; }
162 inline void set_pc(Instruction* new_pc) {
163 pc_ = new_pc;
164 pc_modified_ = true;
167 inline void increment_pc() {
168 if (!pc_modified_) {
169 pc_ = pc_->NextInstruction();
172 pc_modified_ = false;
175 inline void ExecuteInstruction() {
176 // The program counter should always be aligned.
177 assert(IsWordAligned(pc_));
178 decoder_->Decode(pc_);
179 increment_pc();
182 // Declare all Visitor functions.
183 #define DECLARE(A) void Visit##A(Instruction* instr) override;
184 VISITOR_LIST(DECLARE)
185 #undef DECLARE
187 // Register accessors.
188 inline int32_t wreg(unsigned code,
189 Reg31Mode r31mode = Reg31IsZeroRegister) const {
190 assert(code < kNumberOfRegisters);
191 if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
192 return 0;
194 return registers_[code].w;
197 inline int64_t xreg(unsigned code,
198 Reg31Mode r31mode = Reg31IsZeroRegister) const {
199 assert(code < kNumberOfRegisters);
200 if ((code == 31) && (r31mode == Reg31IsZeroRegister)) {
201 return 0;
203 return registers_[code].x;
206 inline int64_t reg(unsigned size,
207 unsigned code,
208 Reg31Mode r31mode = Reg31IsZeroRegister) const {
209 switch (size) {
210 case kWRegSize: return wreg(code, r31mode) & kWRegMask;
211 case kXRegSize: return xreg(code, r31mode);
212 default:
213 not_reached();
214 return 0;
218 inline void set_wreg(unsigned code, int32_t value,
219 Reg31Mode r31mode = Reg31IsZeroRegister) {
220 assert(code < kNumberOfRegisters);
221 if ((code == kZeroRegCode) && (r31mode == Reg31IsZeroRegister)) {
222 return;
224 registers_[code].x = 0; // First clear the register top bits.
225 registers_[code].w = value;
228 template<typename T>
229 inline void set_xreg(unsigned code, T* value,
230 Reg31Mode r31mode = Reg31IsZeroRegister) {
231 set_xreg(code, reinterpret_cast<int64_t>(value), r31mode);
234 inline void set_xreg(unsigned code, int64_t value,
235 Reg31Mode r31mode = Reg31IsZeroRegister) {
236 assert(code < kNumberOfRegisters);
237 if ((code == kZeroRegCode) && (r31mode == Reg31IsZeroRegister)) {
238 return;
240 registers_[code].x = value;
243 inline void set_reg(unsigned size, unsigned code, int64_t value,
244 Reg31Mode r31mode = Reg31IsZeroRegister) {
245 switch (size) {
246 case kWRegSize:
247 return set_wreg(code, static_cast<int32_t>(value & 0xffffffff),
248 r31mode);
249 case kXRegSize:
250 return set_xreg(code, value, r31mode);
251 default:
252 not_reached();
253 break;
257 #define REG_ACCESSORS(N) \
258 inline int32_t w##N() { return wreg(N); } \
259 inline int64_t x##N() { return xreg(N); } \
260 inline void set_w##N(int32_t val) { set_wreg(N, val); } \
261 inline void set_x##N(int64_t val) { set_xreg(N, val); }
262 REGISTER_CODE_LIST(REG_ACCESSORS)
263 #undef REG_ACCESSORS
265 // Aliases.
266 #define REG_ALIAS_ACCESSORS(N, wname, xname) \
267 inline int32_t wname() { return wreg(N); } \
268 inline int64_t xname() { return xreg(N); } \
269 inline void set_##wname(int32_t val) { set_wreg(N, val); } \
270 inline void set_##xname(int64_t val) { set_xreg(N, val); }
271 REG_ALIAS_ACCESSORS(30, wlr, lr);
272 #undef REG_ALIAS_ACCESSORS
274 // The stack is a special case in aarch64.
275 inline int32_t wsp() { return wreg(31, Reg31IsStackPointer); }
276 inline int64_t sp() { return xreg(31, Reg31IsStackPointer); }
277 inline void set_wsp(int32_t val) {
278 set_wreg(31, val, Reg31IsStackPointer);
280 inline void set_sp(int64_t val) {
281 set_xreg(31, val, Reg31IsStackPointer);
284 // FPRegister accessors.
285 inline float sreg(unsigned code) const {
286 assert(code < kNumberOfFPRegisters);
287 return fpregisters_[code].s;
290 inline uint32_t sreg_bits(unsigned code) const {
291 return float_to_rawbits(sreg(code));
294 inline double dreg(unsigned code) const {
295 assert(code < kNumberOfFPRegisters);
296 return fpregisters_[code].d;
299 inline uint64_t dreg_bits(unsigned code) const {
300 return double_to_rawbits(dreg(code));
303 inline double fpreg(unsigned size, unsigned code) const {
304 switch (size) {
305 case kSRegSize: return sreg(code);
306 case kDRegSize: return dreg(code);
307 default: {
308 not_reached();
309 return 0.0;
314 inline void set_sreg(unsigned code, float val) {
315 assert(code < kNumberOfFPRegisters);
316 // Ensure that the upper word is set to 0.
317 set_dreg_bits(code, 0);
319 fpregisters_[code].s = val;
322 inline void set_sreg_bits(unsigned code, uint32_t rawbits) {
323 assert(code < kNumberOfFPRegisters);
324 // Ensure that the upper word is set to 0.
325 set_dreg_bits(code, 0);
327 set_sreg(code, rawbits_to_float(rawbits));
330 inline void set_dreg(unsigned code, double val) {
331 assert(code < kNumberOfFPRegisters);
332 fpregisters_[code].d = val;
335 inline void set_dreg_bits(unsigned code, uint64_t rawbits) {
336 assert(code < kNumberOfFPRegisters);
337 set_dreg(code, rawbits_to_double(rawbits));
340 inline void set_fpreg(unsigned size, unsigned code, double value) {
341 switch (size) {
342 case kSRegSize:
343 return set_sreg(code, value);
344 case kDRegSize:
345 return set_dreg(code, value);
346 default:
347 not_reached();
348 break;
352 #define FPREG_ACCESSORS(N) \
353 inline float s##N() { return sreg(N); } \
354 inline double d##N() { return dreg(N); } \
355 inline void set_s##N(float val) { set_sreg(N, val); } \
356 inline void set_d##N(double val) { set_dreg(N, val); }
357 REGISTER_CODE_LIST(FPREG_ACCESSORS)
358 #undef FPREG_ACCESSORS
360 bool N() { return nzcv_.N() != 0; }
361 bool Z() { return nzcv_.Z() != 0; }
362 bool C() { return nzcv_.C() != 0; }
363 bool V() { return nzcv_.V() != 0; }
364 SimSystemRegister& nzcv() { return nzcv_; }
366 // TODO(jbramley): Find a way to make the fpcr_ members return the proper
367 // types, so this accessor is not necessary.
368 FPRounding RMode() { return static_cast<FPRounding>(fpcr_.RMode()); }
369 SimSystemRegister& fpcr() { return fpcr_; }
371 // Debug helpers
372 void PrintSystemRegisters(bool print_all = false);
373 void PrintRegisters(bool print_all_regs = false);
374 void PrintFPRegisters(bool print_all_regs = false);
375 void PrintProcessorState();
377 static const char* WRegNameForCode(unsigned code,
378 Reg31Mode mode = Reg31IsZeroRegister);
379 static const char* XRegNameForCode(unsigned code,
380 Reg31Mode mode = Reg31IsZeroRegister);
381 static const char* SRegNameForCode(unsigned code);
382 static const char* DRegNameForCode(unsigned code);
383 static const char* VRegNameForCode(unsigned code);
385 inline bool coloured_trace() { return coloured_trace_; }
386 inline void set_coloured_trace(bool value) { coloured_trace_ = value; }
388 inline bool disasm_trace() { return disasm_trace_; }
389 inline void set_disasm_trace(bool value) {
390 if (value != disasm_trace_) {
391 if (value) {
392 decoder_->InsertVisitorBefore(print_disasm_, this);
393 } else {
394 decoder_->RemoveVisitor(print_disasm_);
396 disasm_trace_ = value;
400 bool is_on_stack(void* ptr) const {
401 return uint64_t((byte*)ptr - stack_) < (uint64_t)stack_size_;
404 uint64_t load_count() const { return load_count_; }
405 uint64_t store_count() const { return store_count_; }
406 uint64_t instr_count() const { return instr_count_; }
408 protected:
409 // Simulation helpers ------------------------------------
410 bool ConditionPassed(Condition cond) {
411 switch (cond) {
412 case eq:
413 return Z();
414 case ne:
415 return !Z();
416 case hs:
417 return C();
418 case lo:
419 return !C();
420 case mi:
421 return N();
422 case pl:
423 return !N();
424 case vs:
425 return V();
426 case vc:
427 return !V();
428 case hi:
429 return C() && !Z();
430 case ls:
431 return !(C() && !Z());
432 case ge:
433 return N() == V();
434 case lt:
435 return N() != V();
436 case gt:
437 return !Z() && (N() == V());
438 case le:
439 return !(!Z() && (N() == V()));
440 case nv: // Fall through.
441 case al:
442 return true;
443 default:
444 not_reached();
445 return false;
449 bool ConditionFailed(Condition cond) {
450 return !ConditionPassed(cond);
453 void AddSubHelper(Instruction* instr, int64_t op2);
454 int64_t AddWithCarry(unsigned reg_size,
455 bool set_flags,
456 int64_t src1,
457 int64_t src2,
458 int64_t carry_in = 0);
459 void LogicalHelper(Instruction* instr, int64_t op2);
460 void ConditionalCompareHelper(Instruction* instr, int64_t op2);
461 void LoadStoreHelper(Instruction* instr,
462 int64_t offset,
463 AddrMode addrmode);
464 void LoadStorePairHelper(Instruction* instr, AddrMode addrmode);
465 uint8_t* AddressModeHelper(unsigned addr_reg,
466 int64_t offset,
467 AddrMode addrmode);
469 uint64_t MemoryRead(const uint8_t* address, unsigned num_bytes);
470 uint8_t MemoryRead8(uint8_t* address);
471 uint16_t MemoryRead16(uint8_t* address);
472 uint32_t MemoryRead32(uint8_t* address);
473 float MemoryReadFP32(uint8_t* address);
474 uint64_t MemoryRead64(uint8_t* address);
475 double MemoryReadFP64(uint8_t* address);
477 void MemoryWrite(uint8_t* address, uint64_t value, unsigned num_bytes);
478 void MemoryWrite32(uint8_t* address, uint32_t value);
479 void MemoryWriteFP32(uint8_t* address, float value);
480 void MemoryWrite64(uint8_t* address, uint64_t value);
481 void MemoryWriteFP64(uint8_t* address, double value);
483 int64_t ShiftOperand(unsigned reg_size,
484 int64_t value,
485 Shift shift_type,
486 unsigned amount);
487 int64_t Rotate(unsigned reg_width,
488 int64_t value,
489 Shift shift_type,
490 unsigned amount);
491 int64_t ExtendValue(unsigned reg_width,
492 int64_t value,
493 Extend extend_type,
494 unsigned left_shift = 0);
496 uint64_t ReverseBits(uint64_t value, unsigned num_bits);
497 uint64_t ReverseBytes(uint64_t value, ReverseByteMode mode);
499 void FPCompare(double val0, double val1);
500 double FPRoundInt(double value, FPRounding round_mode);
501 double FPToDouble(float value);
502 float FPToFloat(double value, FPRounding round_mode);
503 double FixedToDouble(int64_t src, int fbits, FPRounding round_mode);
504 double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode);
505 float FixedToFloat(int64_t src, int fbits, FPRounding round_mode);
506 float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode);
507 int32_t FPToInt32(double value, FPRounding rmode);
508 int64_t FPToInt64(double value, FPRounding rmode);
509 uint32_t FPToUInt32(double value, FPRounding rmode);
510 uint64_t FPToUInt64(double value, FPRounding rmode);
511 double FPMax(double a, double b);
512 double FPMin(double a, double b);
514 // Pseudo Printf instruction
515 void DoPrintf(Instruction* instr);
516 // Pseudo HostCall instruction
517 void DoHostCall(Instruction* instr);
519 // Processor state ---------------------------------------
521 // Output stream.
522 std::ostream& stream_;
523 PrintDisassembler* print_disasm_;
525 // Instruction statistics instrumentation.
526 Instrument* instrumentation_;
528 // Hook to handle exceptions being thrown through simulated code.
529 ExceptionHook exception_hook_ = nullptr;
530 std::stack<std::exception_ptr> exns_in_flight_;
532 // General purpose registers. Register 31 is the stack pointer.
533 SimRegister registers_[kNumberOfRegisters];
535 // Floating point registers
536 SimFPRegister fpregisters_[kNumberOfFPRegisters];
538 // Program Status Register.
539 // bits[31, 27]: Condition flags N, Z, C, and V.
540 // (Negative, Zero, Carry, Overflow)
541 SimSystemRegister nzcv_;
543 // Floating-Point Control Register
544 SimSystemRegister fpcr_;
546 // Only a subset of FPCR features are supported by the simulator. This helper
547 // checks that the FPCR settings are supported.
549 // This is checked when floating-point instructions are executed, not when
550 // FPCR is set. This allows generated code to modify FPCR for external
551 // functions, or to save and restore it when entering and leaving generated
552 // code.
553 void AssertSupportedFPCR() {
554 assert(fpcr().DN() == 0); // No default-NaN support.
555 assert(fpcr().FZ() == 0); // No flush-to-zero support.
556 assert(fpcr().RMode() == FPTieEven); // Ties-to-even rounding only.
558 // The simulator does not support half-precision operations so fpcr().AHP()
559 // is irrelevant, and is not checked here.
562 static inline int CalcNFlag(uint64_t result, unsigned reg_size) {
563 return (result >> (reg_size - 1)) & 1;
566 static inline int CalcZFlag(uint64_t result) {
567 return result == 0;
570 static const uint32_t kConditionFlagsMask = 0xf0000000;
572 // Stack
573 byte* stack_;
574 static const int stack_protection_size_ = 256;
575 // 512 KB stack.
576 static const int stack_size_ = (512 << 10) + 2 * stack_protection_size_;
577 byte* stack_limit_;
579 Decoder* decoder_;
580 // Indicates if the pc has been modified by the instruction and should not be
581 // automatically incremented.
582 bool pc_modified_;
583 Instruction* pc_;
585 // Counters for basic simulated-system events. Note that the load counter does
586 // not count instruction fetches, only explicit loads.
587 uint64_t load_count_ = 0;
588 uint64_t store_count_ = 0;
589 uint64_t instr_count_ = 0;
591 static const char* xreg_names[];
592 static const char* wreg_names[];
593 static const char* sreg_names[];
594 static const char* dreg_names[];
595 static const char* vreg_names[];
597 static const Instruction* kEndOfSimAddress;
599 private:
600 bool coloured_trace_;
602 // Indicates whether the disassembly trace is active.
603 bool disasm_trace_;
605 } // namespace vixl
607 #endif // VIXL_A64_SIMULATOR_A64_H_