1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Rrdistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Rrdistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Rrdistributions 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 <stdlib.h>
29 
30 #include "src/v8.h"
31 
32 #include "src/base/platform/platform.h"
33 #include "src/code-stubs.h"
34 #include "src/factory.h"
35 #include "src/macro-assembler.h"
36 #include "src/simulator.h"
37 #include "test/cctest/cctest.h"
38 #include "test/cctest/test-code-stubs.h"
39 
40 using namespace v8::internal;
41 
42 #define __ masm.
43 
MakeConvertDToIFuncTrampoline(Isolate * isolate,Register source_reg,Register destination_reg,bool inline_fastpath)44 ConvertDToIFunc MakeConvertDToIFuncTrampoline(Isolate* isolate,
45                                               Register source_reg,
46                                               Register destination_reg,
47                                               bool inline_fastpath) {
48   // Allocate an executable page of memory.
49   size_t actual_size = 4 * Assembler::kMinimalBufferSize;
50   byte* buffer = static_cast<byte*>(
51       v8::base::OS::Allocate(actual_size, &actual_size, true));
52   CHECK(buffer);
53   HandleScope handles(isolate);
54   MacroAssembler masm(isolate, buffer, static_cast<int>(actual_size));
55   DoubleToIStub stub(isolate, source_reg, destination_reg, 0, true,
56                      inline_fastpath);
57 
58   byte* start = stub.GetCode()->instruction_start();
59   Label done;
60 
61   __ SetStackPointer(csp);
62   __ PushCalleeSavedRegisters();
63   __ Mov(jssp, csp);
64   __ SetStackPointer(jssp);
65 
66   // Push the double argument.
67   __ Push(d0);
68   __ Mov(source_reg, jssp);
69 
70   MacroAssembler::PushPopQueue queue(&masm);
71 
72   // Save registers make sure they don't get clobbered.
73   int source_reg_offset = kDoubleSize;
74   int reg_num = 0;
75   for (;reg_num < Register::NumAllocatableRegisters(); ++reg_num) {
76     Register reg = Register::from_code(reg_num);
77     if (!reg.is(destination_reg)) {
78       queue.Queue(reg);
79       source_reg_offset += kPointerSize;
80     }
81   }
82   // Re-push the double argument.
83   queue.Queue(d0);
84 
85   queue.PushQueued();
86 
87   // Call through to the actual stub
88   if (inline_fastpath) {
89     __ Ldr(d0, MemOperand(source_reg));
90     __ TryConvertDoubleToInt64(destination_reg, d0, &done);
91     if (destination_reg.is(source_reg)) {
92       // Restore clobbered source_reg.
93       __ add(source_reg, jssp, Operand(source_reg_offset));
94     }
95   }
96   __ Call(start, RelocInfo::EXTERNAL_REFERENCE);
97   __ bind(&done);
98 
99   __ Drop(1, kDoubleSize);
100 
101   // // Make sure no registers have been unexpectedly clobbered
102   for (--reg_num; reg_num >= 0; --reg_num) {
103     Register reg = Register::from_code(reg_num);
104     if (!reg.is(destination_reg)) {
105       __ Pop(ip0);
106       __ cmp(reg, ip0);
107       __ Assert(eq, kRegisterWasClobbered);
108     }
109   }
110 
111   __ Drop(1, kDoubleSize);
112 
113   if (!destination_reg.is(x0))
114     __ Mov(x0, destination_reg);
115 
116   // Restore callee save registers.
117   __ Mov(csp, jssp);
118   __ SetStackPointer(csp);
119   __ PopCalleeSavedRegisters();
120 
121   __ Ret();
122 
123   CodeDesc desc;
124   masm.GetCode(&desc);
125   CpuFeatures::FlushICache(buffer, actual_size);
126   return (reinterpret_cast<ConvertDToIFunc>(
127       reinterpret_cast<intptr_t>(buffer)));
128 }
129 
130 #undef __
131 
132 
GetIsolateFrom(LocalContext * context)133 static Isolate* GetIsolateFrom(LocalContext* context) {
134   return reinterpret_cast<Isolate*>((*context)->GetIsolate());
135 }
136 
137 
RunGeneratedCodeCallWrapper(ConvertDToIFunc func,double from)138 int32_t RunGeneratedCodeCallWrapper(ConvertDToIFunc func,
139                                     double from) {
140 #ifdef USE_SIMULATOR
141   Simulator::CallArgument args[] = {
142       Simulator::CallArgument(from),
143       Simulator::CallArgument::End()
144   };
145   return Simulator::current(Isolate::Current())->CallInt64(
146       FUNCTION_ADDR(func), args);
147 #else
148   return (*func)(from);
149 #endif
150 }
151 
152 
TEST(ConvertDToI)153 TEST(ConvertDToI) {
154   CcTest::InitializeVM();
155   LocalContext context;
156   Isolate* isolate = GetIsolateFrom(&context);
157   HandleScope scope(isolate);
158 
159 #if DEBUG
160   // Verify that the tests actually work with the C version. In the release
161   // code, the compiler optimizes it away because it's all constant, but does it
162   // wrong, triggering an assert on gcc.
163   RunAllTruncationTests(&ConvertDToICVersion);
164 #endif
165 
166   Register source_registers[] = {jssp, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9,
167                                  x10, x11, x12, x13, x14, x15, x18, x19, x20,
168                                  x21, x22, x23, x24};
169   Register dest_registers[] = {x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11,
170                                x12, x13, x14, x15, x18, x19, x20, x21, x22, x23,
171                                x24};
172 
173   for (size_t s = 0; s < sizeof(source_registers) / sizeof(Register); s++) {
174     for (size_t d = 0; d < sizeof(dest_registers) / sizeof(Register); d++) {
175       RunAllTruncationTests(
176           RunGeneratedCodeCallWrapper,
177           MakeConvertDToIFuncTrampoline(isolate,
178                                         source_registers[s],
179                                         dest_registers[d],
180                                         false));
181       RunAllTruncationTests(
182           RunGeneratedCodeCallWrapper,
183           MakeConvertDToIFuncTrampoline(isolate,
184                                         source_registers[s],
185                                         dest_registers[d],
186                                         true));
187     }
188   }
189 }
190