blob: bcb51c42d3ea26d6a285b499f91b396cfb6ff7b5 [file] [log] [blame]
buzbeeefc63692012-11-14 16:31:52 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* This file contains codegen for the X86 ISA */
18
19namespace art {
20
21/*
22 * Perform register memory operation.
23 */
24LIR* genRegMemCheck(CompilationUnit* cUnit, ConditionCode cCode,
25 int reg1, int base, int offset, ThrowKind kind)
26{
27 LIR* tgt = rawLIR(cUnit, 0, kPseudoThrowTarget, kind,
28 cUnit->currentDalvikOffset, reg1, base, offset);
29 opRegMem(cUnit, kOpCmp, reg1, base, offset);
30 LIR* branch = opCondBranch(cUnit, cCode, tgt);
31 // Remember branch target - will process later
buzbeecbd6d442012-11-17 14:11:25 -080032 oatInsertGrowableList(cUnit, &cUnit->throwLaunchpads, reinterpret_cast<uintptr_t>(tgt));
buzbeeefc63692012-11-14 16:31:52 -080033 return branch;
34}
35
36/*
37 * Compare two 64-bit values
38 * x = y return 0
39 * x < y return -1
40 * x > y return 1
41 *
42 * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0
43 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0
44 * subu res, t0, t1 # res = -1:1:0 for [ < > = ]
45 * bnez res, finish
46 * sltu t0, x.lo, y.lo
47 * sgtu r1, x.lo, y.lo
48 * subu res, t0, t1
49 * finish:
50 *
51 */
52void genCmpLong(CompilationUnit* cUnit, RegLocation rlDest,
53 RegLocation rlSrc1, RegLocation rlSrc2)
54{
55 oatFlushAllRegs(cUnit);
56 oatLockCallTemps(cUnit); // Prepare for explicit register usage
57 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
58 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
59 // Compute (r1:r0) = (r1:r0) - (r3:r2)
60 opRegReg(cUnit, kOpSub, r0, r2); // r0 = r0 - r2
61 opRegReg(cUnit, kOpSbc, r1, r3); // r1 = r1 - r3 - CF
62 newLIR2(cUnit, kX86Set8R, r2, kX86CondL); // r2 = (r1:r0) < (r3:r2) ? 1 : 0
63 newLIR2(cUnit, kX86Movzx8RR, r2, r2);
64 opReg(cUnit, kOpNeg, r2); // r2 = -r2
65 opRegReg(cUnit, kOpOr, r0, r1); // r0 = high | low - sets ZF
66 newLIR2(cUnit, kX86Set8R, r0, kX86CondNz); // r0 = (r1:r0) != (r3:r2) ? 1 : 0
67 newLIR2(cUnit, kX86Movzx8RR, r0, r0);
68 opRegReg(cUnit, kOpOr, r0, r2); // r0 = r0 | r2
69 RegLocation rlResult = locCReturn();
70 storeValue(cUnit, rlDest, rlResult);
71}
72
73X86ConditionCode oatX86ConditionEncoding(ConditionCode cond) {
74 switch (cond) {
75 case kCondEq: return kX86CondEq;
76 case kCondNe: return kX86CondNe;
77 case kCondCs: return kX86CondC;
78 case kCondCc: return kX86CondNc;
79 case kCondMi: return kX86CondS;
80 case kCondPl: return kX86CondNs;
81 case kCondVs: return kX86CondO;
82 case kCondVc: return kX86CondNo;
83 case kCondHi: return kX86CondA;
84 case kCondLs: return kX86CondBe;
85 case kCondGe: return kX86CondGe;
86 case kCondLt: return kX86CondL;
87 case kCondGt: return kX86CondG;
88 case kCondLe: return kX86CondLe;
89 case kCondAl:
90 case kCondNv: LOG(FATAL) << "Should not reach here";
91 }
92 return kX86CondO;
93}
94
95LIR* opCmpBranch(CompilationUnit* cUnit, ConditionCode cond, int src1,
96 int src2, LIR* target)
97{
98 newLIR2(cUnit, kX86Cmp32RR, src1, src2);
99 X86ConditionCode cc = oatX86ConditionEncoding(cond);
100 LIR* branch = newLIR2(cUnit, kX86Jcc8, 0 /* lir operand for Jcc offset */ ,
101 cc);
102 branch->target = target;
103 return branch;
104}
105
106LIR* opCmpImmBranch(CompilationUnit* cUnit, ConditionCode cond, int reg,
107 int checkValue, LIR* target)
108{
109 if ((checkValue == 0) && (cond == kCondEq || cond == kCondNe)) {
110 // TODO: when checkValue == 0 and reg is rCX, use the jcxz/nz opcode
111 newLIR2(cUnit, kX86Test32RR, reg, reg);
112 } else {
113 newLIR2(cUnit, IS_SIMM8(checkValue) ? kX86Cmp32RI8 : kX86Cmp32RI, reg, checkValue);
114 }
115 X86ConditionCode cc = oatX86ConditionEncoding(cond);
116 LIR* branch = newLIR2(cUnit, kX86Jcc8, 0 /* lir operand for Jcc offset */ , cc);
117 branch->target = target;
118 return branch;
119}
120
121LIR* opRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
122{
123 if (X86_FPREG(rDest) || X86_FPREG(rSrc))
124 return fpRegCopy(cUnit, rDest, rSrc);
125 LIR* res = rawLIR(cUnit, cUnit->currentDalvikOffset, kX86Mov32RR,
126 rDest, rSrc);
127 if (rDest == rSrc) {
128 res->flags.isNop = true;
129 }
130 return res;
131}
132
133LIR* opRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
134{
135 LIR *res = opRegCopyNoInsert(cUnit, rDest, rSrc);
136 oatAppendLIR(cUnit, res);
137 return res;
138}
139
140void opRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
141 int srcLo, int srcHi)
142{
143 bool destFP = X86_FPREG(destLo) && X86_FPREG(destHi);
144 bool srcFP = X86_FPREG(srcLo) && X86_FPREG(srcHi);
145 assert(X86_FPREG(srcLo) == X86_FPREG(srcHi));
146 assert(X86_FPREG(destLo) == X86_FPREG(destHi));
147 if (destFP) {
148 if (srcFP) {
149 opRegCopy(cUnit, s2d(destLo, destHi), s2d(srcLo, srcHi));
150 } else {
151 // TODO: Prevent this from happening in the code. The result is often
152 // unused or could have been loaded more easily from memory.
153 newLIR2(cUnit, kX86MovdxrRR, destLo, srcLo);
154 newLIR2(cUnit, kX86MovdxrRR, destHi, srcHi);
155 newLIR2(cUnit, kX86PsllqRI, destHi, 32);
156 newLIR2(cUnit, kX86OrpsRR, destLo, destHi);
157 }
158 } else {
159 if (srcFP) {
160 newLIR2(cUnit, kX86MovdrxRR, destLo, srcLo);
161 newLIR2(cUnit, kX86PsrlqRI, srcLo, 32);
162 newLIR2(cUnit, kX86MovdrxRR, destHi, srcLo);
163 } else {
164 // Handle overlap
165 if (srcHi == destLo) {
166 opRegCopy(cUnit, destHi, srcHi);
167 opRegCopy(cUnit, destLo, srcLo);
168 } else {
169 opRegCopy(cUnit, destLo, srcLo);
170 opRegCopy(cUnit, destHi, srcHi);
171 }
172 }
173 }
174}
175
176void genFusedLongCmpBranch(CompilationUnit* cUnit, BasicBlock* bb, MIR* mir) {
177 LIR* labelList = cUnit->blockLabelList;
178 LIR* taken = &labelList[bb->taken->id];
179 RegLocation rlSrc1 = oatGetSrcWide(cUnit, mir, 0);
180 RegLocation rlSrc2 = oatGetSrcWide(cUnit, mir, 2);
181 oatFlushAllRegs(cUnit);
182 oatLockCallTemps(cUnit); // Prepare for explicit register usage
183 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
184 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
185 ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
186 // Swap operands and condition code to prevent use of zero flag.
187 if (ccode == kCondLe || ccode == kCondGt) {
188 // Compute (r3:r2) = (r3:r2) - (r1:r0)
189 opRegReg(cUnit, kOpSub, r2, r0); // r2 = r2 - r0
190 opRegReg(cUnit, kOpSbc, r3, r1); // r3 = r3 - r1 - CF
191 } else {
192 // Compute (r1:r0) = (r1:r0) - (r3:r2)
193 opRegReg(cUnit, kOpSub, r0, r2); // r0 = r0 - r2
194 opRegReg(cUnit, kOpSbc, r1, r3); // r1 = r1 - r3 - CF
195 }
196 switch (ccode) {
197 case kCondEq:
198 case kCondNe:
199 opRegReg(cUnit, kOpOr, r0, r1); // r0 = r0 | r1
200 break;
201 case kCondLe:
202 ccode = kCondGe;
203 break;
204 case kCondGt:
205 ccode = kCondLt;
206 break;
207 case kCondLt:
208 case kCondGe:
209 break;
210 default:
buzbeecbd6d442012-11-17 14:11:25 -0800211 LOG(FATAL) << "Unexpected ccode: " << ccode;
buzbeeefc63692012-11-14 16:31:52 -0800212 }
213 opCondBranch(cUnit, ccode, taken);
214}
215RegLocation genDivRemLit(CompilationUnit* cUnit, RegLocation rlDest, int regLo, int lit, bool isDiv)
216{
217 LOG(FATAL) << "Unexpected use of genDivRemLit for x86";
218 return rlDest;
219}
220
221RegLocation genDivRem(CompilationUnit* cUnit, RegLocation rlDest, int regLo, int regHi, bool isDiv)
222{
223 LOG(FATAL) << "Unexpected use of genDivRem for x86";
224 return rlDest;
225}
226
227bool genInlinedMinMaxInt(CompilationUnit *cUnit, CallInfo* info, bool isMin)
228{
229 DCHECK_EQ(cUnit->instructionSet, kX86);
230 RegLocation rlSrc1 = info->args[0];
231 RegLocation rlSrc2 = info->args[1];
232 rlSrc1 = loadValue(cUnit, rlSrc1, kCoreReg);
233 rlSrc2 = loadValue(cUnit, rlSrc2, kCoreReg);
234 RegLocation rlDest = inlineTarget(cUnit, info);
235 RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
236 opRegReg(cUnit, kOpCmp, rlSrc1.lowReg, rlSrc2.lowReg);
237 DCHECK_EQ(cUnit->instructionSet, kX86);
238 LIR* branch = newLIR2(cUnit, kX86Jcc8, 0, isMin ? kX86CondG : kX86CondL);
239 opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc1.lowReg);
240 LIR* branch2 = newLIR1(cUnit, kX86Jmp8, 0);
241 branch->target = newLIR0(cUnit, kPseudoTargetLabel);
242 opRegReg(cUnit, kOpMov, rlResult.lowReg, rlSrc2.lowReg);
243 branch2->target = newLIR0(cUnit, kPseudoTargetLabel);
244 storeValue(cUnit, rlDest, rlResult);
245 return true;
246}
247
248void opLea(CompilationUnit* cUnit, int rBase, int reg1, int reg2, int scale, int offset)
249{
250 newLIR5(cUnit, kX86Lea32RA, rBase, reg1, reg2, scale, offset);
251}
252
253void opTlsCmp(CompilationUnit* cUnit, int offset, int val)
254{
255 newLIR2(cUnit, kX86Cmp16TI8, offset, val);
256}
257
258bool genInlinedCas32(CompilationUnit* cUnit, CallInfo* info, bool need_write_barrier) {
259 DCHECK_NE(cUnit->instructionSet, kThumb2);
260 return false;
261}
262
263LIR* opPcRelLoad(CompilationUnit* cUnit, int reg, LIR* target) {
264 LOG(FATAL) << "Unexpected use of opPcRelLoad for x86";
265 return NULL;
266}
267
268LIR* opVldm(CompilationUnit* cUnit, int rBase, int count)
269{
270 LOG(FATAL) << "Unexpected use of opVldm for x86";
271 return NULL;
272}
273
274LIR* opVstm(CompilationUnit* cUnit, int rBase, int count)
275{
276 LOG(FATAL) << "Unexpected use of opVstm for x86";
277 return NULL;
278}
279
280void genMultiplyByTwoBitMultiplier(CompilationUnit* cUnit, RegLocation rlSrc,
281 RegLocation rlResult, int lit,
282 int firstBit, int secondBit)
283{
284 int tReg = oatAllocTemp(cUnit);
285 opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, secondBit - firstBit);
286 opRegRegReg(cUnit, kOpAdd, rlResult.lowReg, rlSrc.lowReg, tReg);
287 oatFreeTemp(cUnit, tReg);
288 if (firstBit != 0) {
289 opRegRegImm(cUnit, kOpLsl, rlResult.lowReg, rlResult.lowReg, firstBit);
290 }
291}
292
293void genDivZeroCheck(CompilationUnit* cUnit, int regLo, int regHi)
294{
295 int tReg = oatAllocTemp(cUnit);
296 opRegRegReg(cUnit, kOpOr, tReg, regLo, regHi);
297 genImmedCheck(cUnit, kCondEq, tReg, 0, kThrowDivZero);
298 oatFreeTemp(cUnit, tReg);
299}
300
301// Test suspend flag, return target of taken suspend branch
302LIR* opTestSuspend(CompilationUnit* cUnit, LIR* target)
303{
304 opTlsCmp(cUnit, Thread::ThreadFlagsOffset().Int32Value(), 0);
305 return opCondBranch(cUnit, (target == NULL) ? kCondNe : kCondEq, target);
306}
307
308// Decrement register and branch on condition
309LIR* opDecAndBranch(CompilationUnit* cUnit, ConditionCode cCode, int reg, LIR* target)
310{
311 opRegImm(cUnit, kOpSub, reg, 1);
312 return opCmpImmBranch(cUnit, cCode, reg, 0, target);
313}
314
315bool smallLiteralDivide(CompilationUnit* cUnit, Instruction::Code dalvikOpcode,
316 RegLocation rlSrc, RegLocation rlDest, int lit)
317{
318 LOG(FATAL) << "Unexpected use of smallLiteralDive in x86";
319 return false;
320}
321
322LIR* opIT(CompilationUnit* cUnit, ArmConditionCode cond, const char* guide)
323{
324 LOG(FATAL) << "Unexpected use of opIT in x86";
325 return NULL;
326}
327bool genAddLong(CompilationUnit* cUnit, RegLocation rlDest,
328 RegLocation rlSrc1, RegLocation rlSrc2)
329{
330 oatFlushAllRegs(cUnit);
331 oatLockCallTemps(cUnit); // Prepare for explicit register usage
332 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
333 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
334 // Compute (r1:r0) = (r1:r0) + (r2:r3)
335 opRegReg(cUnit, kOpAdd, r0, r2); // r0 = r0 + r2
336 opRegReg(cUnit, kOpAdc, r1, r3); // r1 = r1 + r3 + CF
337 RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
338 INVALID_SREG, INVALID_SREG};
339 storeValueWide(cUnit, rlDest, rlResult);
340 return false;
341}
342
343bool genSubLong(CompilationUnit* cUnit, RegLocation rlDest,
344 RegLocation rlSrc1, RegLocation rlSrc2)
345{
346 oatFlushAllRegs(cUnit);
347 oatLockCallTemps(cUnit); // Prepare for explicit register usage
348 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
349 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
350 // Compute (r1:r0) = (r1:r0) + (r2:r3)
351 opRegReg(cUnit, kOpSub, r0, r2); // r0 = r0 - r2
352 opRegReg(cUnit, kOpSbc, r1, r3); // r1 = r1 - r3 - CF
353 RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
354 INVALID_SREG, INVALID_SREG};
355 storeValueWide(cUnit, rlDest, rlResult);
356 return false;
357}
358
359bool genAndLong(CompilationUnit* cUnit, RegLocation rlDest,
360 RegLocation rlSrc1, RegLocation rlSrc2)
361{
362 oatFlushAllRegs(cUnit);
363 oatLockCallTemps(cUnit); // Prepare for explicit register usage
364 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
365 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
366 // Compute (r1:r0) = (r1:r0) + (r2:r3)
367 opRegReg(cUnit, kOpAnd, r0, r2); // r0 = r0 - r2
368 opRegReg(cUnit, kOpAnd, r1, r3); // r1 = r1 - r3 - CF
369 RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
370 INVALID_SREG, INVALID_SREG};
371 storeValueWide(cUnit, rlDest, rlResult);
372 return false;
373}
374
375bool genOrLong(CompilationUnit* cUnit, RegLocation rlDest,
376 RegLocation rlSrc1, RegLocation rlSrc2)
377{
378 oatFlushAllRegs(cUnit);
379 oatLockCallTemps(cUnit); // Prepare for explicit register usage
380 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
381 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
382 // Compute (r1:r0) = (r1:r0) + (r2:r3)
383 opRegReg(cUnit, kOpOr, r0, r2); // r0 = r0 - r2
384 opRegReg(cUnit, kOpOr, r1, r3); // r1 = r1 - r3 - CF
385 RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
386 INVALID_SREG, INVALID_SREG};
387 storeValueWide(cUnit, rlDest, rlResult);
388 return false;
389}
390
391bool genXorLong(CompilationUnit* cUnit, RegLocation rlDest,
392 RegLocation rlSrc1, RegLocation rlSrc2)
393{
394 oatFlushAllRegs(cUnit);
395 oatLockCallTemps(cUnit); // Prepare for explicit register usage
396 loadValueDirectWideFixed(cUnit, rlSrc1, r0, r1);
397 loadValueDirectWideFixed(cUnit, rlSrc2, r2, r3);
398 // Compute (r1:r0) = (r1:r0) + (r2:r3)
399 opRegReg(cUnit, kOpXor, r0, r2); // r0 = r0 - r2
400 opRegReg(cUnit, kOpXor, r1, r3); // r1 = r1 - r3 - CF
401 RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
402 INVALID_SREG, INVALID_SREG};
403 storeValueWide(cUnit, rlDest, rlResult);
404 return false;
405}
406
407bool genNegLong(CompilationUnit* cUnit, RegLocation rlDest,
408 RegLocation rlSrc)
409{
410 oatFlushAllRegs(cUnit);
411 oatLockCallTemps(cUnit); // Prepare for explicit register usage
412 loadValueDirectWideFixed(cUnit, rlSrc, r0, r1);
413 // Compute (r1:r0) = -(r1:r0)
414 opRegReg(cUnit, kOpNeg, r0, r0); // r0 = -r0
415 opRegImm(cUnit, kOpAdc, r1, 0); // r1 = r1 + CF
416 opRegReg(cUnit, kOpNeg, r1, r1); // r1 = -r1
417 RegLocation rlResult = {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, r0, r1,
418 INVALID_SREG, INVALID_SREG};
419 storeValueWide(cUnit, rlDest, rlResult);
420 return false;
421}
422
423void opRegThreadMem(CompilationUnit* cUnit, OpKind op, int rDest, int threadOffset) {
424 X86OpCode opcode = kX86Bkpt;
425 switch (op) {
426 case kOpCmp: opcode = kX86Cmp32RT; break;
427 default:
428 LOG(FATAL) << "Bad opcode: " << op;
429 break;
430 }
431 newLIR2(cUnit, opcode, rDest, threadOffset);
432}
433
434} // namespace art