OLD | NEW |
| (Empty) |
1 // Copyright 2013 the V8 project authors. All rights reserved. | |
2 // Redistribution and use in source and binary forms, with or without | |
3 // modification, are permitted provided that the following conditions are | |
4 // met: | |
5 // | |
6 // * Redistributions of source code must retain the above copyright | |
7 // notice, this list of conditions and the following disclaimer. | |
8 // * Redistributions in binary form must reproduce the above | |
9 // copyright notice, this list of conditions and the following | |
10 // disclaimer in the documentation and/or other materials provided | |
11 // with the distribution. | |
12 // * Neither the name of Google Inc. nor the names of its | |
13 // contributors may be used to endorse or promote products derived | |
14 // from this software without specific prior written permission. | |
15 // | |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 #include "v8.h" | |
29 | |
30 #include "macro-assembler.h" | |
31 #include "a64/utils-a64.h" | |
32 #include "cctest.h" | |
33 #include "test-utils-a64.h" | |
34 | |
35 using namespace v8::internal; | |
36 | |
37 | |
38 #define __ masm-> | |
39 | |
40 | |
41 bool Equal32(uint32_t expected, const RegisterDump*, uint32_t result) { | |
42 if (result != expected) { | |
43 printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n", | |
44 expected, result); | |
45 } | |
46 | |
47 return expected == result; | |
48 } | |
49 | |
50 | |
51 bool Equal64(uint64_t expected, const RegisterDump*, uint64_t result) { | |
52 if (result != expected) { | |
53 printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", | |
54 expected, result); | |
55 } | |
56 | |
57 return expected == result; | |
58 } | |
59 | |
60 | |
61 bool EqualFP32(float expected, const RegisterDump*, float result) { | |
62 if (float_to_rawbits(expected) == float_to_rawbits(result)) { | |
63 return true; | |
64 } else { | |
65 if (std::isnan(expected) || (expected == 0.0)) { | |
66 printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n", | |
67 float_to_rawbits(expected), float_to_rawbits(result)); | |
68 } else { | |
69 printf("Expected %.9f (0x%08" PRIx32 ")\t " | |
70 "Found %.9f (0x%08" PRIx32 ")\n", | |
71 expected, float_to_rawbits(expected), | |
72 result, float_to_rawbits(result)); | |
73 } | |
74 return false; | |
75 } | |
76 } | |
77 | |
78 | |
79 bool EqualFP64(double expected, const RegisterDump*, double result) { | |
80 if (double_to_rawbits(expected) == double_to_rawbits(result)) { | |
81 return true; | |
82 } | |
83 | |
84 if (std::isnan(expected) || (expected == 0.0)) { | |
85 printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", | |
86 double_to_rawbits(expected), double_to_rawbits(result)); | |
87 } else { | |
88 printf("Expected %.17f (0x%016" PRIx64 ")\t " | |
89 "Found %.17f (0x%016" PRIx64 ")\n", | |
90 expected, double_to_rawbits(expected), | |
91 result, double_to_rawbits(result)); | |
92 } | |
93 return false; | |
94 } | |
95 | |
96 | |
97 bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg) { | |
98 ASSERT(reg.Is32Bits()); | |
99 // Retrieve the corresponding X register so we can check that the upper part | |
100 // was properly cleared. | |
101 int64_t result_x = core->xreg(reg.code()); | |
102 if ((result_x & 0xffffffff00000000L) != 0) { | |
103 printf("Expected 0x%08" PRIx32 "\t Found 0x%016" PRIx64 "\n", | |
104 expected, result_x); | |
105 return false; | |
106 } | |
107 uint32_t result_w = core->wreg(reg.code()); | |
108 return Equal32(expected, core, result_w); | |
109 } | |
110 | |
111 | |
112 bool Equal64(uint64_t expected, | |
113 const RegisterDump* core, | |
114 const Register& reg) { | |
115 ASSERT(reg.Is64Bits()); | |
116 uint64_t result = core->xreg(reg.code()); | |
117 return Equal64(expected, core, result); | |
118 } | |
119 | |
120 | |
121 bool EqualFP32(float expected, | |
122 const RegisterDump* core, | |
123 const FPRegister& fpreg) { | |
124 ASSERT(fpreg.Is32Bits()); | |
125 // Retrieve the corresponding D register so we can check that the upper part | |
126 // was properly cleared. | |
127 uint64_t result_64 = core->dreg_bits(fpreg.code()); | |
128 if ((result_64 & 0xffffffff00000000L) != 0) { | |
129 printf("Expected 0x%08" PRIx32 " (%f)\t Found 0x%016" PRIx64 "\n", | |
130 float_to_rawbits(expected), expected, result_64); | |
131 return false; | |
132 } | |
133 | |
134 return EqualFP32(expected, core, core->sreg(fpreg.code())); | |
135 } | |
136 | |
137 | |
138 bool EqualFP64(double expected, | |
139 const RegisterDump* core, | |
140 const FPRegister& fpreg) { | |
141 ASSERT(fpreg.Is64Bits()); | |
142 return EqualFP64(expected, core, core->dreg(fpreg.code())); | |
143 } | |
144 | |
145 | |
146 bool Equal64(const Register& reg0, | |
147 const RegisterDump* core, | |
148 const Register& reg1) { | |
149 ASSERT(reg0.Is64Bits() && reg1.Is64Bits()); | |
150 int64_t expected = core->xreg(reg0.code()); | |
151 int64_t result = core->xreg(reg1.code()); | |
152 return Equal64(expected, core, result); | |
153 } | |
154 | |
155 | |
156 static char FlagN(uint32_t flags) { | |
157 return (flags & NFlag) ? 'N' : 'n'; | |
158 } | |
159 | |
160 | |
161 static char FlagZ(uint32_t flags) { | |
162 return (flags & ZFlag) ? 'Z' : 'z'; | |
163 } | |
164 | |
165 | |
166 static char FlagC(uint32_t flags) { | |
167 return (flags & CFlag) ? 'C' : 'c'; | |
168 } | |
169 | |
170 | |
171 static char FlagV(uint32_t flags) { | |
172 return (flags & VFlag) ? 'V' : 'v'; | |
173 } | |
174 | |
175 | |
176 bool EqualNzcv(uint32_t expected, uint32_t result) { | |
177 ASSERT((expected & ~NZCVFlag) == 0); | |
178 ASSERT((result & ~NZCVFlag) == 0); | |
179 if (result != expected) { | |
180 printf("Expected: %c%c%c%c\t Found: %c%c%c%c\n", | |
181 FlagN(expected), FlagZ(expected), FlagC(expected), FlagV(expected), | |
182 FlagN(result), FlagZ(result), FlagC(result), FlagV(result)); | |
183 return false; | |
184 } | |
185 | |
186 return true; | |
187 } | |
188 | |
189 | |
190 bool EqualRegisters(const RegisterDump* a, const RegisterDump* b) { | |
191 for (unsigned i = 0; i < kNumberOfRegisters; i++) { | |
192 if (a->xreg(i) != b->xreg(i)) { | |
193 printf("x%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", | |
194 i, a->xreg(i), b->xreg(i)); | |
195 return false; | |
196 } | |
197 } | |
198 | |
199 for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { | |
200 uint64_t a_bits = a->dreg_bits(i); | |
201 uint64_t b_bits = b->dreg_bits(i); | |
202 if (a_bits != b_bits) { | |
203 printf("d%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", | |
204 i, a_bits, b_bits); | |
205 return false; | |
206 } | |
207 } | |
208 | |
209 return true; | |
210 } | |
211 | |
212 | |
213 RegList PopulateRegisterArray(Register* w, Register* x, Register* r, | |
214 int reg_size, int reg_count, RegList allowed) { | |
215 RegList list = 0; | |
216 int i = 0; | |
217 for (unsigned n = 0; (n < kNumberOfRegisters) && (i < reg_count); n++) { | |
218 if (((1UL << n) & allowed) != 0) { | |
219 // Only assign allowed registers. | |
220 if (r) { | |
221 r[i] = Register::Create(n, reg_size); | |
222 } | |
223 if (x) { | |
224 x[i] = Register::Create(n, kXRegSizeInBits); | |
225 } | |
226 if (w) { | |
227 w[i] = Register::Create(n, kWRegSizeInBits); | |
228 } | |
229 list |= (1UL << n); | |
230 i++; | |
231 } | |
232 } | |
233 // Check that we got enough registers. | |
234 ASSERT(CountSetBits(list, kNumberOfRegisters) == reg_count); | |
235 | |
236 return list; | |
237 } | |
238 | |
239 | |
240 RegList PopulateFPRegisterArray(FPRegister* s, FPRegister* d, FPRegister* v, | |
241 int reg_size, int reg_count, RegList allowed) { | |
242 RegList list = 0; | |
243 int i = 0; | |
244 for (unsigned n = 0; (n < kNumberOfFPRegisters) && (i < reg_count); n++) { | |
245 if (((1UL << n) & allowed) != 0) { | |
246 // Only assigned allowed registers. | |
247 if (v) { | |
248 v[i] = FPRegister::Create(n, reg_size); | |
249 } | |
250 if (d) { | |
251 d[i] = FPRegister::Create(n, kDRegSizeInBits); | |
252 } | |
253 if (s) { | |
254 s[i] = FPRegister::Create(n, kSRegSizeInBits); | |
255 } | |
256 list |= (1UL << n); | |
257 i++; | |
258 } | |
259 } | |
260 // Check that we got enough registers. | |
261 ASSERT(CountSetBits(list, kNumberOfFPRegisters) == reg_count); | |
262 | |
263 return list; | |
264 } | |
265 | |
266 | |
267 void Clobber(MacroAssembler* masm, RegList reg_list, uint64_t const value) { | |
268 Register first = NoReg; | |
269 for (unsigned i = 0; i < kNumberOfRegisters; i++) { | |
270 if (reg_list & (1UL << i)) { | |
271 Register xn = Register::Create(i, kXRegSizeInBits); | |
272 // We should never write into csp here. | |
273 ASSERT(!xn.Is(csp)); | |
274 if (!xn.IsZero()) { | |
275 if (!first.IsValid()) { | |
276 // This is the first register we've hit, so construct the literal. | |
277 __ Mov(xn, value); | |
278 first = xn; | |
279 } else { | |
280 // We've already loaded the literal, so re-use the value already | |
281 // loaded into the first register we hit. | |
282 __ Mov(xn, first); | |
283 } | |
284 } | |
285 } | |
286 } | |
287 } | |
288 | |
289 | |
290 void ClobberFP(MacroAssembler* masm, RegList reg_list, double const value) { | |
291 FPRegister first = NoFPReg; | |
292 for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { | |
293 if (reg_list & (1UL << i)) { | |
294 FPRegister dn = FPRegister::Create(i, kDRegSizeInBits); | |
295 if (!first.IsValid()) { | |
296 // This is the first register we've hit, so construct the literal. | |
297 __ Fmov(dn, value); | |
298 first = dn; | |
299 } else { | |
300 // We've already loaded the literal, so re-use the value already loaded | |
301 // into the first register we hit. | |
302 __ Fmov(dn, first); | |
303 } | |
304 } | |
305 } | |
306 } | |
307 | |
308 | |
309 void Clobber(MacroAssembler* masm, CPURegList reg_list) { | |
310 if (reg_list.type() == CPURegister::kRegister) { | |
311 // This will always clobber X registers. | |
312 Clobber(masm, reg_list.list()); | |
313 } else if (reg_list.type() == CPURegister::kFPRegister) { | |
314 // This will always clobber D registers. | |
315 ClobberFP(masm, reg_list.list()); | |
316 } else { | |
317 UNREACHABLE(); | |
318 } | |
319 } | |
320 | |
321 | |
322 void RegisterDump::Dump(MacroAssembler* masm) { | |
323 ASSERT(__ StackPointer().Is(csp)); | |
324 | |
325 // Ensure that we don't unintentionally clobber any registers. | |
326 RegList old_tmp_list = masm->TmpList()->list(); | |
327 RegList old_fptmp_list = masm->FPTmpList()->list(); | |
328 masm->TmpList()->set_list(0); | |
329 masm->FPTmpList()->set_list(0); | |
330 | |
331 // Preserve some temporary registers. | |
332 Register dump_base = x0; | |
333 Register dump = x1; | |
334 Register tmp = x2; | |
335 Register dump_base_w = dump_base.W(); | |
336 Register dump_w = dump.W(); | |
337 Register tmp_w = tmp.W(); | |
338 | |
339 // Offsets into the dump_ structure. | |
340 const int x_offset = offsetof(dump_t, x_); | |
341 const int w_offset = offsetof(dump_t, w_); | |
342 const int d_offset = offsetof(dump_t, d_); | |
343 const int s_offset = offsetof(dump_t, s_); | |
344 const int sp_offset = offsetof(dump_t, sp_); | |
345 const int wsp_offset = offsetof(dump_t, wsp_); | |
346 const int flags_offset = offsetof(dump_t, flags_); | |
347 | |
348 __ Push(xzr, dump_base, dump, tmp); | |
349 | |
350 // Load the address where we will dump the state. | |
351 __ Mov(dump_base, reinterpret_cast<uint64_t>(&dump_)); | |
352 | |
353 // Dump the stack pointer (csp and wcsp). | |
354 // The stack pointer cannot be stored directly; it needs to be moved into | |
355 // another register first. Also, we pushed four X registers, so we need to | |
356 // compensate here. | |
357 __ Add(tmp, csp, 4 * kXRegSize); | |
358 __ Str(tmp, MemOperand(dump_base, sp_offset)); | |
359 __ Add(tmp_w, wcsp, 4 * kXRegSize); | |
360 __ Str(tmp_w, MemOperand(dump_base, wsp_offset)); | |
361 | |
362 // Dump X registers. | |
363 __ Add(dump, dump_base, x_offset); | |
364 for (unsigned i = 0; i < kNumberOfRegisters; i += 2) { | |
365 __ Stp(Register::XRegFromCode(i), Register::XRegFromCode(i + 1), | |
366 MemOperand(dump, i * kXRegSize)); | |
367 } | |
368 | |
369 // Dump W registers. | |
370 __ Add(dump, dump_base, w_offset); | |
371 for (unsigned i = 0; i < kNumberOfRegisters; i += 2) { | |
372 __ Stp(Register::WRegFromCode(i), Register::WRegFromCode(i + 1), | |
373 MemOperand(dump, i * kWRegSize)); | |
374 } | |
375 | |
376 // Dump D registers. | |
377 __ Add(dump, dump_base, d_offset); | |
378 for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) { | |
379 __ Stp(FPRegister::DRegFromCode(i), FPRegister::DRegFromCode(i + 1), | |
380 MemOperand(dump, i * kDRegSize)); | |
381 } | |
382 | |
383 // Dump S registers. | |
384 __ Add(dump, dump_base, s_offset); | |
385 for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) { | |
386 __ Stp(FPRegister::SRegFromCode(i), FPRegister::SRegFromCode(i + 1), | |
387 MemOperand(dump, i * kSRegSize)); | |
388 } | |
389 | |
390 // Dump the flags. | |
391 __ Mrs(tmp, NZCV); | |
392 __ Str(tmp, MemOperand(dump_base, flags_offset)); | |
393 | |
394 // To dump the values that were in tmp amd dump, we need a new scratch | |
395 // register. We can use any of the already dumped registers since we can | |
396 // easily restore them. | |
397 Register dump2_base = x10; | |
398 Register dump2 = x11; | |
399 ASSERT(!AreAliased(dump_base, dump, tmp, dump2_base, dump2)); | |
400 | |
401 // Don't lose the dump_ address. | |
402 __ Mov(dump2_base, dump_base); | |
403 | |
404 __ Pop(tmp, dump, dump_base, xzr); | |
405 | |
406 __ Add(dump2, dump2_base, w_offset); | |
407 __ Str(dump_base_w, MemOperand(dump2, dump_base.code() * kWRegSize)); | |
408 __ Str(dump_w, MemOperand(dump2, dump.code() * kWRegSize)); | |
409 __ Str(tmp_w, MemOperand(dump2, tmp.code() * kWRegSize)); | |
410 | |
411 __ Add(dump2, dump2_base, x_offset); | |
412 __ Str(dump_base, MemOperand(dump2, dump_base.code() * kXRegSize)); | |
413 __ Str(dump, MemOperand(dump2, dump.code() * kXRegSize)); | |
414 __ Str(tmp, MemOperand(dump2, tmp.code() * kXRegSize)); | |
415 | |
416 // Finally, restore dump2_base and dump2. | |
417 __ Ldr(dump2_base, MemOperand(dump2, dump2_base.code() * kXRegSize)); | |
418 __ Ldr(dump2, MemOperand(dump2, dump2.code() * kXRegSize)); | |
419 | |
420 // Restore the MacroAssembler's scratch registers. | |
421 masm->TmpList()->set_list(old_tmp_list); | |
422 masm->FPTmpList()->set_list(old_fptmp_list); | |
423 | |
424 completed_ = true; | |
425 } | |
OLD | NEW |