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