1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jit/mips64/CodeGenerator-mips64.h"
8
9 #include "mozilla/MathAlgorithms.h"
10
11 #include "jit/CodeGenerator.h"
12 #include "jit/JitCompartment.h"
13 #include "jit/JitFrames.h"
14 #include "jit/MIR.h"
15 #include "jit/MIRGraph.h"
16 #include "js/Conversions.h"
17 #include "vm/Shape.h"
18 #include "vm/TraceLogging.h"
19
20 #include "jit/MacroAssembler-inl.h"
21 #include "jit/shared/CodeGenerator-shared-inl.h"
22
23 using namespace js;
24 using namespace js::jit;
25
26 class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorMIPS64>
27 {
28 MTableSwitch* mir_;
29 CodeLabel jumpLabel_;
30
accept(CodeGeneratorMIPS64 * codegen)31 void accept(CodeGeneratorMIPS64* codegen) {
32 codegen->visitOutOfLineTableSwitch(this);
33 }
34
35 public:
OutOfLineTableSwitch(MTableSwitch * mir)36 OutOfLineTableSwitch(MTableSwitch* mir)
37 : mir_(mir)
38 {}
39
mir() const40 MTableSwitch* mir() const {
41 return mir_;
42 }
43
jumpLabel()44 CodeLabel* jumpLabel() {
45 return &jumpLabel_;
46 }
47 };
48
49 void
visitOutOfLineBailout(OutOfLineBailout * ool)50 CodeGeneratorMIPS64::visitOutOfLineBailout(OutOfLineBailout* ool)
51 {
52 masm.push(ImmWord(ool->snapshot()->snapshotOffset()));
53
54 masm.jump(&deoptLabel_);
55 }
56
57 void
visitOutOfLineTableSwitch(OutOfLineTableSwitch * ool)58 CodeGeneratorMIPS64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
59 {
60 MTableSwitch* mir = ool->mir();
61
62 masm.haltingAlign(sizeof(void*));
63 masm.bind(ool->jumpLabel()->target());
64 masm.addCodeLabel(*ool->jumpLabel());
65
66 for (size_t i = 0; i < mir->numCases(); i++) {
67 LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir();
68 Label* caseheader = caseblock->label();
69 uint32_t caseoffset = caseheader->offset();
70
71 // The entries of the jump table need to be absolute addresses and thus
72 // must be patched after codegen is finished. Each table entry uses 8
73 // instructions (4 for load address, 2 for branch, and 2 padding).
74 CodeLabel cl;
75 masm.ma_li(ScratchRegister, cl.patchAt());
76 masm.branch(ScratchRegister);
77 masm.as_nop();
78 masm.as_nop();
79 cl.target()->bind(caseoffset);
80 masm.addCodeLabel(cl);
81 }
82 }
83
84 void
emitTableSwitchDispatch(MTableSwitch * mir,Register index,Register address)85 CodeGeneratorMIPS64::emitTableSwitchDispatch(MTableSwitch* mir, Register index,
86 Register address)
87 {
88 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
89
90 // Lower value with low value
91 if (mir->low() != 0)
92 masm.subPtr(Imm32(mir->low()), index);
93
94 // Jump to default case if input is out of range
95 int32_t cases = mir->numCases();
96 masm.branch32(Assembler::AboveOrEqual, index, Imm32(cases), defaultcase);
97
98 // To fill in the CodeLabels for the case entries, we need to first
99 // generate the case entries (we don't yet know their offsets in the
100 // instruction stream).
101 OutOfLineTableSwitch* ool = new(alloc()) OutOfLineTableSwitch(mir);
102 addOutOfLineCode(ool, mir);
103
104 // Compute the position where a pointer to the right case stands.
105 masm.ma_li(address, ool->jumpLabel()->patchAt());
106 // index = size of table entry * index.
107 // See CodeGeneratorMIPS64::visitOutOfLineTableSwitch
108 masm.lshiftPtr(Imm32(5), index);
109 masm.addPtr(index, address);
110
111 masm.branch(address);
112 }
113
114 FrameSizeClass
FromDepth(uint32_t frameDepth)115 FrameSizeClass::FromDepth(uint32_t frameDepth)
116 {
117 return FrameSizeClass::None();
118 }
119
120 FrameSizeClass
ClassLimit()121 FrameSizeClass::ClassLimit()
122 {
123 return FrameSizeClass(0);
124 }
125
126 uint32_t
frameSize() const127 FrameSizeClass::frameSize() const
128 {
129 MOZ_CRASH("MIPS64 does not use frame size classes");
130 }
131
132 ValueOperand
ToValue(LInstruction * ins,size_t pos)133 CodeGeneratorMIPS64::ToValue(LInstruction* ins, size_t pos)
134 {
135 return ValueOperand(ToRegister(ins->getOperand(pos)));
136 }
137
138 ValueOperand
ToOutValue(LInstruction * ins)139 CodeGeneratorMIPS64::ToOutValue(LInstruction* ins)
140 {
141 return ValueOperand(ToRegister(ins->getDef(0)));
142 }
143
144 ValueOperand
ToTempValue(LInstruction * ins,size_t pos)145 CodeGeneratorMIPS64::ToTempValue(LInstruction* ins, size_t pos)
146 {
147 return ValueOperand(ToRegister(ins->getTemp(pos)));
148 }
149
150 void
visitBox(LBox * box)151 CodeGeneratorMIPS64::visitBox(LBox* box)
152 {
153 const LAllocation* in = box->getOperand(0);
154 const LDefinition* result = box->getDef(0);
155
156 if (IsFloatingPointType(box->type())) {
157 FloatRegister reg = ToFloatRegister(in);
158 if (box->type() == MIRType_Float32) {
159 masm.convertFloat32ToDouble(reg, ScratchDoubleReg);
160 reg = ScratchDoubleReg;
161 }
162 masm.moveFromDouble(reg, ToRegister(result));
163 } else {
164 masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
165 }
166 }
167
168 void
visitUnbox(LUnbox * unbox)169 CodeGeneratorMIPS64::visitUnbox(LUnbox* unbox)
170 {
171 MUnbox* mir = unbox->mir();
172
173 if (mir->fallible()) {
174 const ValueOperand value = ToValue(unbox, LUnbox::Input);
175 masm.splitTag(value, SecondScratchReg);
176 bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(MIRTypeToTag(mir->type())),
177 unbox->snapshot());
178 }
179
180 Operand input = ToOperand(unbox->getOperand(LUnbox::Input));
181 Register result = ToRegister(unbox->output());
182 switch (mir->type()) {
183 case MIRType_Int32:
184 masm.unboxInt32(input, result);
185 break;
186 case MIRType_Boolean:
187 masm.unboxBoolean(input, result);
188 break;
189 case MIRType_Object:
190 masm.unboxObject(input, result);
191 break;
192 case MIRType_String:
193 masm.unboxString(input, result);
194 break;
195 case MIRType_Symbol:
196 masm.unboxSymbol(input, result);
197 break;
198 default:
199 MOZ_CRASH("Given MIRType cannot be unboxed.");
200 }
201 }
202
203 Register
splitTagForTest(const ValueOperand & value)204 CodeGeneratorMIPS64::splitTagForTest(const ValueOperand& value)
205 {
206 MOZ_ASSERT(value.valueReg() != SecondScratchReg);
207 masm.splitTag(value.valueReg(), SecondScratchReg);
208 return SecondScratchReg;
209 }
210
211 void
visitCompareB(LCompareB * lir)212 CodeGeneratorMIPS64::visitCompareB(LCompareB* lir)
213 {
214 MCompare* mir = lir->mir();
215
216 const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
217 const LAllocation* rhs = lir->rhs();
218 const Register output = ToRegister(lir->output());
219
220 MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
221 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
222
223 // Load boxed boolean in ScratchRegister.
224 if (rhs->isConstant())
225 masm.moveValue(*rhs->toConstant(), ScratchRegister);
226 else
227 masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
228
229 // Perform the comparison.
230 masm.cmpPtrSet(cond, lhs.valueReg(), ScratchRegister, output);
231 }
232
233 void
visitCompareBAndBranch(LCompareBAndBranch * lir)234 CodeGeneratorMIPS64::visitCompareBAndBranch(LCompareBAndBranch* lir)
235 {
236 MCompare* mir = lir->cmpMir();
237 const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
238 const LAllocation* rhs = lir->rhs();
239
240 MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
241
242 // Load boxed boolean in ScratchRegister.
243 if (rhs->isConstant())
244 masm.moveValue(*rhs->toConstant(), ScratchRegister);
245 else
246 masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
247
248 // Perform the comparison.
249 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
250 emitBranch(lhs.valueReg(), ScratchRegister, cond, lir->ifTrue(), lir->ifFalse());
251 }
252
253 void
visitCompareBitwise(LCompareBitwise * lir)254 CodeGeneratorMIPS64::visitCompareBitwise(LCompareBitwise* lir)
255 {
256 MCompare* mir = lir->mir();
257 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
258 const ValueOperand lhs = ToValue(lir, LCompareBitwise::LhsInput);
259 const ValueOperand rhs = ToValue(lir, LCompareBitwise::RhsInput);
260 const Register output = ToRegister(lir->output());
261
262 MOZ_ASSERT(IsEqualityOp(mir->jsop()));
263
264 masm.cmpPtrSet(cond, lhs.valueReg(), rhs.valueReg(), output);
265 }
266
267 void
visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch * lir)268 CodeGeneratorMIPS64::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
269 {
270 MCompare* mir = lir->cmpMir();
271 Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
272 const ValueOperand lhs = ToValue(lir, LCompareBitwiseAndBranch::LhsInput);
273 const ValueOperand rhs = ToValue(lir, LCompareBitwiseAndBranch::RhsInput);
274
275 MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
276 mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
277
278 emitBranch(lhs.valueReg(), rhs.valueReg(), cond, lir->ifTrue(), lir->ifFalse());
279 }
280
281 void
setReturnDoubleRegs(LiveRegisterSet * regs)282 CodeGeneratorMIPS64::setReturnDoubleRegs(LiveRegisterSet* regs)
283 {
284 MOZ_ASSERT(ReturnFloat32Reg.reg_ == FloatRegisters::f0);
285 MOZ_ASSERT(ReturnDoubleReg.reg_ == FloatRegisters::f0);
286 FloatRegister f1 = { FloatRegisters::f1, FloatRegisters::Single };
287 regs->add(ReturnFloat32Reg);
288 regs->add(f1);
289 regs->add(ReturnDoubleReg);
290 }
291