Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / assembler / assembler / ARMAssembler.cpp
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=79:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Copyright (C) 2009 University of Szeged
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * 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  * ***** END LICENSE BLOCK ***** */
30
31 #include "assembler/wtf/Platform.h"
32
33 #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL
34
35 #include "ARMAssembler.h"
36
37 namespace JSC {
38
39 // Patching helpers
40
41 void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr)
42 {
43     ARMWord *ldr = reinterpret_cast<ARMWord*>(loadAddr);
44     ARMWord diff = reinterpret_cast<ARMWord*>(constPoolAddr) - ldr;
45     ARMWord index = (*ldr & 0xfff) >> 1;
46
47     ASSERT(diff >= 1);
48     if (diff >= 2 || index > 0) {
49         diff = (diff + index - 2) * sizeof(ARMWord);
50         ASSERT(diff <= 0xfff);
51         *ldr = (*ldr & ~0xfff) | diff;
52     } else
53         *ldr = (*ldr & ~(0xfff | ARMAssembler::DT_UP)) | sizeof(ARMWord);
54 }
55
56 // Handle immediates
57
58 ARMWord ARMAssembler::getOp2(ARMWord imm)
59 {
60     int rol;
61
62     if (imm <= 0xff)
63         return OP2_IMM | imm;
64
65     if ((imm & 0xff000000) == 0) {
66         imm <<= 8;
67         rol = 8;
68     }
69     else {
70         imm = (imm << 24) | (imm >> 8);
71         rol = 0;
72     }
73
74     if ((imm & 0xff000000) == 0) {
75         imm <<= 8;
76         rol += 4;
77     }
78
79     if ((imm & 0xf0000000) == 0) {
80         imm <<= 4;
81         rol += 2;
82     }
83
84     if ((imm & 0xc0000000) == 0) {
85         imm <<= 2;
86         rol += 1;
87     }
88
89     if ((imm & 0x00ffffff) == 0)
90         return OP2_IMM | (imm >> 24) | (rol << 8);
91
92     return INVALID_IMM;
93 }
94
95 int ARMAssembler::genInt(int reg, ARMWord imm, bool positive)
96 {
97     // Step1: Search a non-immediate part
98     ARMWord mask;
99     ARMWord imm1;
100     ARMWord imm2;
101     int rol;
102
103     mask = 0xff000000;
104     rol = 8;
105     while(1) {
106         if ((imm & mask) == 0) {
107             imm = (imm << rol) | (imm >> (32 - rol));
108             rol = 4 + (rol >> 1);
109             break;
110         }
111         rol += 2;
112         mask >>= 2;
113         if (mask & 0x3) {
114             // rol 8
115             imm = (imm << 8) | (imm >> 24);
116             mask = 0xff00;
117             rol = 24;
118             while (1) {
119                 if ((imm & mask) == 0) {
120                     imm = (imm << rol) | (imm >> (32 - rol));
121                     rol = (rol >> 1) - 8;
122                     break;
123                 }
124                 rol += 2;
125                 mask >>= 2;
126                 if (mask & 0x3)
127                     return 0;
128             }
129             break;
130         }
131     }
132
133     ASSERT((imm & 0xff) == 0);
134
135     if ((imm & 0xff000000) == 0) {
136         imm1 = OP2_IMM | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8);
137         imm2 = OP2_IMM | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8);
138     } else if (imm & 0xc0000000) {
139         imm1 = OP2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
140         imm <<= 8;
141         rol += 4;
142
143         if ((imm & 0xff000000) == 0) {
144             imm <<= 8;
145             rol += 4;
146         }
147
148         if ((imm & 0xf0000000) == 0) {
149             imm <<= 4;
150             rol += 2;
151         }
152
153         if ((imm & 0xc0000000) == 0) {
154             imm <<= 2;
155             rol += 1;
156         }
157
158         if ((imm & 0x00ffffff) == 0)
159             imm2 = OP2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
160         else
161             return 0;
162     } else {
163         if ((imm & 0xf0000000) == 0) {
164             imm <<= 4;
165             rol += 2;
166         }
167
168         if ((imm & 0xc0000000) == 0) {
169             imm <<= 2;
170             rol += 1;
171         }
172
173         imm1 = OP2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
174         imm <<= 8;
175         rol += 4;
176
177         if ((imm & 0xf0000000) == 0) {
178             imm <<= 4;
179             rol += 2;
180         }
181
182         if ((imm & 0xc0000000) == 0) {
183             imm <<= 2;
184             rol += 1;
185         }
186
187         if ((imm & 0x00ffffff) == 0)
188             imm2 = OP2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
189         else
190             return 0;
191     }
192
193     if (positive) {
194         mov_r(reg, imm1);
195         orr_r(reg, reg, imm2);
196     } else {
197         mvn_r(reg, imm1);
198         bic_r(reg, reg, imm2);
199     }
200
201     return 1;
202 }
203
204 #ifdef __GNUC__
205 // If the result of this function isn't used, the caller should probably be
206 // using movImm.
207 __attribute__((warn_unused_result))
208 #endif
209 ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert)
210 {
211     ARMWord tmp;
212
213     // Do it by 1 instruction
214     tmp = getOp2(imm);
215     if (tmp != INVALID_IMM)
216         return tmp;
217
218     tmp = getOp2(~imm);
219     if (tmp != INVALID_IMM) {
220         if (invert)
221             return tmp | OP2_INV_IMM;
222         mvn_r(tmpReg, tmp);
223         return tmpReg;
224     }
225
226     return encodeComplexImm(imm, tmpReg);
227 }
228
229 void ARMAssembler::moveImm(ARMWord imm, int dest)
230 {
231     ARMWord tmp;
232
233     // Do it by 1 instruction
234     tmp = getOp2(imm);
235     if (tmp != INVALID_IMM) {
236         mov_r(dest, tmp);
237         return;
238     }
239
240     tmp = getOp2(~imm);
241     if (tmp != INVALID_IMM) {
242         mvn_r(dest, tmp);
243         return;
244     }
245
246     encodeComplexImm(imm, dest);
247 }
248
249 ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest)
250 {
251 #if WTF_ARM_ARCH_VERSION >= 7
252     ARMWord tmp = getImm16Op2(imm);
253     if (tmp != INVALID_IMM) {
254         movw_r(dest, tmp);
255         return dest;
256     }
257     movw_r(dest, getImm16Op2(imm & 0xffff));
258     movt_r(dest, getImm16Op2(imm >> 16));
259     return dest;
260 #else
261     // Do it by 2 instruction
262     if (genInt(dest, imm, true))
263         return dest;
264     if (genInt(dest, ~imm, false))
265         return dest;
266
267     ldr_imm(dest, imm);
268     return dest;
269 #endif
270 }
271
272 // Memory load/store helpers
273
274 void ARMAssembler::dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset)
275 {
276     if (offset >= 0) {
277         if (offset <= 0xfff)
278             dtr_u(isLoad, srcDst, base, offset);
279         else if (offset <= 0xfffff) {
280             add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
281             dtr_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
282         } else {
283             moveImm(offset, ARMRegisters::S0);
284             dtr_ur(isLoad, srcDst, base, ARMRegisters::S0);
285         }
286     } else {
287         offset = -offset;
288         if (offset <= 0xfff)
289             dtr_d(isLoad, srcDst, base, offset);
290         else if (offset <= 0xfffff) {
291             sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
292             dtr_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
293         } else {
294             moveImm(offset, ARMRegisters::S0);
295             dtr_dr(isLoad, srcDst, base, ARMRegisters::S0);
296         }
297     }
298 }
299
300 void ARMAssembler::dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset)
301 {
302     if (offset >= 0) {
303         if (offset <= 0xfff)
304             dtrb_u(isLoad, srcDst, base, offset);
305         else if (offset <= 0xfffff) {
306             add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
307             dtrb_u(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
308         } else {
309             moveImm(offset, ARMRegisters::S0);
310             dtrb_ur(isLoad, srcDst, base, ARMRegisters::S0);
311         }
312     } else {
313         offset = -offset;
314         if (offset <= 0xfff)
315             dtrb_d(isLoad, srcDst, base, offset);
316         else if (offset <= 0xfffff) {
317             sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 12) | (10 << 8));
318             dtrb_d(isLoad, srcDst, ARMRegisters::S0, (offset & 0xfff));
319         } else {
320             moveImm(offset, ARMRegisters::S0);
321             dtrb_dr(isLoad, srcDst, base, ARMRegisters::S0);
322         }
323     }
324 }
325
326 void ARMAssembler::baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset)
327 {
328     ARMWord op2;
329
330     ASSERT(scale >= 0 && scale <= 3);
331     op2 = lsl(index, scale);
332
333     if (offset >= 0 && offset <= 0xfff) {
334         add_r(ARMRegisters::S0, base, op2);
335         dtr_u(isLoad, srcDst, ARMRegisters::S0, offset);
336         return;
337     }
338     if (offset <= 0 && offset >= -0xfff) {
339         add_r(ARMRegisters::S0, base, op2);
340         dtr_d(isLoad, srcDst, ARMRegisters::S0, -offset);
341         return;
342     }
343
344     ldr_un_imm(ARMRegisters::S0, offset);
345     add_r(ARMRegisters::S0, ARMRegisters::S0, op2);
346     dtr_ur(isLoad, srcDst, base, ARMRegisters::S0);
347 }
348
349 void ARMAssembler::doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset)
350 {
351     if (offset & 0x3) {
352         if (offset <= 0x3ff && offset >= 0) {
353             fdtr_u(isLoad, srcDst, base, offset >> 2);
354             return;
355         }
356         if (offset <= 0x3ffff && offset >= 0) {
357             add_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | (11 << 8));
358             fdtr_u(isLoad, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff);
359             return;
360         }
361         offset = -offset;
362
363         if (offset <= 0x3ff && offset >= 0) {
364             fdtr_d(isLoad, srcDst, base, offset >> 2);
365             return;
366         }
367         if (offset <= 0x3ffff && offset >= 0) {
368             sub_r(ARMRegisters::S0, base, OP2_IMM | (offset >> 10) | (11 << 8));
369             fdtr_d(isLoad, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff);
370             return;
371         }
372         offset = -offset;
373     }
374
375     // TODO: This is broken in the case that offset is unaligned. VFP can never
376     // perform unaligned accesses, even from an unaligned register base. (NEON
377     // can, but VFP isn't NEON. It is not advisable to interleave a NEON load
378     // with VFP code, so the best solution here is probably to perform an
379     // unaligned integer load, then move the result into VFP using VMOV.)
380     ASSERT((offset & 0x3) == 0);
381
382     ldr_un_imm(ARMRegisters::S0, offset);
383     add_r(ARMRegisters::S0, ARMRegisters::S0, base);
384     fdtr_u(isLoad, srcDst, ARMRegisters::S0, 0);
385 }
386
387 // Fix up the offsets and literal-pool loads in buffer. The buffer should
388 // already contain the code from m_buffer.
389 inline void ARMAssembler::fixUpOffsets(void * buffer)
390 {
391     char * data = reinterpret_cast<char *>(buffer);
392     for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) {
393         // The last bit is set if the constant must be placed on constant pool.
394         int pos = (*iter) & (~0x1);
395         ARMWord* ldrAddr = reinterpret_cast<ARMWord*>(data + pos);
396         ARMWord* addr = getLdrImmAddress(ldrAddr);
397         if (*addr != InvalidBranchTarget) {
398 // The following is disabled for JM because we patch some branches after
399 // calling fixUpOffset, and the branch patcher doesn't know how to handle 'B'
400 // instructions.
401 #if 0
402             if (!(*iter & 1)) {
403                 int diff = reinterpret_cast<ARMWord*>(data + *addr) - (ldrAddr + DefaultPrefetching);
404
405                 if ((diff <= BOFFSET_MAX && diff >= BOFFSET_MIN)) {
406                     *ldrAddr = B | getConditionalField(*ldrAddr) | (diff & BRANCH_MASK);
407                     continue;
408                 }
409             }
410 #endif
411             *addr = reinterpret_cast<ARMWord>(data + *addr);
412         }
413     }
414 }
415
416 void* ARMAssembler::executableCopy(ExecutablePool* allocator)
417 {
418     // 64-bit alignment is required for next constant pool and JIT code as well
419     m_buffer.flushWithoutBarrier(true);
420     if (m_buffer.uncheckedSize() & 0x7)
421         bkpt(0);
422
423     void * data = m_buffer.executableCopy(allocator);
424     if (data)
425         fixUpOffsets(data);
426     return data;
427 }
428
429 // This just dumps the code into the specified buffer, fixing up absolute
430 // offsets and literal pool loads as it goes. The buffer is assumed to be large
431 // enough to hold the code, and any pre-existing literal pool is assumed to
432 // have been flushed.
433 void* ARMAssembler::executableCopy(void * buffer)
434 {
435     if (m_buffer.oom())
436         return NULL;
437
438     ASSERT(m_buffer.sizeOfConstantPool() == 0);
439
440     memcpy(buffer, m_buffer.data(), m_buffer.size());
441     fixUpOffsets(buffer);
442     return buffer;
443 }
444
445 } // namespace JSC
446
447 #endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)