1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "x64_MPISel.h"
17 #include "x64_isa_tbl.h"
18 #include "x64_cg.h"
19
20 namespace maplebe {
21 /* Field-ID 0 is assigned to the top level structure. (Field-ID also defaults to 0 if it is not a structure.) */
GetOrCreateMemOpndFromSymbol(const MIRSymbol & symbol,FieldID fieldId) const22 MemOperand &X64MPIsel::GetOrCreateMemOpndFromSymbol(const MIRSymbol &symbol, FieldID fieldId) const
23 {
24 PrimType symType;
25 int32 fieldOffset = 0;
26 CHECK_FATAL(fieldId == 0, "fieldId must be 0");
27 symType = symbol.GetType()->GetPrimType();
28 uint32 opndSz = GetPrimTypeBitSize(symType);
29 return GetOrCreateMemOpndFromSymbol(symbol, opndSz, fieldOffset);
30 }
GetOrCreateMemOpndFromSymbol(const MIRSymbol & symbol,uint32 opndSize,int64 offset) const31 MemOperand &X64MPIsel::GetOrCreateMemOpndFromSymbol(const MIRSymbol &symbol, uint32 opndSize, int64 offset) const
32 {
33 MIRStorageClass storageClass = symbol.GetStorageClass();
34 MemOperand *result = nullptr;
35 RegOperand *stackBaseReg = nullptr;
36 if ((storageClass == kScAuto) || (storageClass == kScFormal)) {
37 auto *symloc = static_cast<X64SymbolAlloc *>(cgFunc->GetMemlayout()->GetSymAllocInfo(symbol.GetStIndex()));
38 DEBUG_ASSERT(symloc != nullptr, "sym loc should have been defined");
39 stackBaseReg = static_cast<X64CGFunc *>(cgFunc)->GetBaseReg(*symloc);
40 int stOfst = cgFunc->GetBaseOffset(*symloc);
41 /* Create field symbols in aggregate structure */
42 result = &GetCurFunc()->GetOpndBuilder()->CreateMem(opndSize);
43 result->SetBaseRegister(*stackBaseReg);
44 result->SetOffsetOperand(GetCurFunc()->GetOpndBuilder()->CreateImm(k64BitSize, stOfst + offset));
45 CHECK_FATAL(result != nullptr, "NIY");
46 return *result;
47 }
48 if ((storageClass == kScGlobal) || (storageClass == kScExtern) || (storageClass == kScPstatic) ||
49 (storageClass == kScFstatic)) {
50 stackBaseReg = &GetCurFunc()->GetOpndBuilder()->CreatePReg(x64::RIP, k64BitSize, kRegTyInt);
51 result = &GetCurFunc()->GetOpndBuilder()->CreateMem(opndSize);
52 ImmOperand &stOfstOpnd = GetCurFunc()->GetOpndBuilder()->CreateImm(symbol, offset, 0);
53 result->SetBaseRegister(*stackBaseReg);
54 result->SetOffsetOperand(stOfstOpnd);
55 CHECK_FATAL(result != nullptr, "NIY");
56 return *result;
57 }
58 CHECK_FATAL(false, "NIY");
59 return *result;
60 }
61
SelectReturn(NaryStmtNode & retNode,Operand & opnd)62 void X64MPIsel::SelectReturn(NaryStmtNode &retNode, Operand &opnd)
63 {
64 MIRType *retType = cgFunc->GetFunction().GetReturnType();
65 X64CallConvImpl retLocator(cgFunc->GetBecommon());
66 CCLocInfo retMech;
67 retLocator.LocateRetVal(*retType, retMech);
68 if (retMech.GetRegCount() == 0) {
69 return;
70 }
71 std::vector<RegOperand *> retRegs;
72 PrimType oriPrimType = retMech.GetPrimTypeOfReg0();
73 regno_t retReg = retMech.GetReg0();
74 DEBUG_ASSERT(retReg != kRinvalid, "NIY");
75 RegOperand &retOpnd = cgFunc->GetOpndBuilder()->CreatePReg(retReg, GetPrimTypeBitSize(oriPrimType),
76 cgFunc->GetRegTyFromPrimTy(oriPrimType));
77 retRegs.push_back(&retOpnd);
78 SelectCopy(retOpnd, opnd, oriPrimType, retNode.Opnd(0)->GetPrimType());
79 /* for optimization ,insert pseudo ret ,in case rax,rdx is removed*/
80 SelectPseduoForReturn(retRegs);
81 }
82
SelectPseduoForReturn(std::vector<RegOperand * > & retRegs)83 void X64MPIsel::SelectPseduoForReturn(std::vector<RegOperand *> &retRegs)
84 {
85 for (auto retReg : retRegs) {
86 MOperator mop = x64::MOP_pseudo_ret_int;
87 Insn &pInsn = cgFunc->GetInsnBuilder()->BuildInsn(mop, X64CG::kMd[mop]);
88 cgFunc->GetCurBB()->AppendInsn(pInsn);
89 pInsn.AddOpndChain(*retReg);
90 }
91 }
92
SelectReturn()93 void X64MPIsel::SelectReturn()
94 {
95 /* jump to epilogue */
96 MOperator mOp = x64::MOP_jmpq_l;
97 LabelNode *endLabel = cgFunc->GetEndLabel();
98 auto endLabelName = ".L." + std::to_string(cgFunc->GetUniqueID()) + "__" + std::to_string(endLabel->GetLabelIdx());
99 LabelOperand &targetOpnd = cgFunc->GetOpndBuilder()->CreateLabel(endLabelName.c_str(), endLabel->GetLabelIdx());
100 Insn &jmpInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
101 jmpInsn.AddOpndChain(targetOpnd);
102 cgFunc->GetCurBB()->AppendInsn(jmpInsn);
103 cgFunc->GetExitBBsVec().emplace_back(cgFunc->GetCurBB());
104 }
105
106 /*
107 * SelectParmList generates an instrunction for each of the parameters
108 * to load the parameter value into the corresponding register.
109 * We return a list of registers to the call instruction because
110 * they may be needed in the register allocation phase.
111 * fp Num is a return value which is the number of vector
112 * registers used;
113 */
SelectParmList(StmtNode & naryNode,ListOperand & srcOpnds,uint32 & fpNum)114 void X64MPIsel::SelectParmList(StmtNode &naryNode, ListOperand &srcOpnds, uint32 &fpNum)
115 {
116 paramPassByReg.clear();
117 fpNum = 0;
118 /* for IcallNode, the 0th operand is the function pointer */
119 size_t argBegin = 0;
120 if (naryNode.GetOpCode() == OP_icall || naryNode.GetOpCode() == OP_icallproto ||
121 naryNode.GetOpCode() == OP_tailicall) {
122 ++argBegin;
123 }
124
125 MIRFunction *callee = nullptr;
126 if (naryNode.GetOpCode() == OP_call) {
127 PUIdx calleePuIdx = static_cast<CallNode &>(naryNode).GetPUIdx();
128 callee = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(calleePuIdx);
129 }
130 X64CallConvImpl parmLocator(cgFunc->GetBecommon(), X64CallConvImpl::GetCallConvKind(naryNode));
131 CCLocInfo ploc;
132 for (size_t i = argBegin; i < naryNode.NumOpnds(); ++i) {
133 BaseNode *argExpr = naryNode.Opnd(i);
134 DEBUG_ASSERT(argExpr != nullptr, "not null check");
135 PrimType primType = argExpr->GetPrimType();
136 DEBUG_ASSERT(primType != PTY_void, "primType should not be void");
137 bool isArgUnused = (callee != nullptr && callee->GetFuncDesc().IsArgUnused(i));
138
139 Operand *argOpnd = HandleExpr(naryNode, *argExpr);
140 DEBUG_ASSERT(argOpnd != nullptr, "not null check");
141 MIRType *mirType = GlobalTables::GetTypeTable().GetTypeTable()[static_cast<uint32>(primType)];
142 parmLocator.LocateNextParm(*mirType, ploc);
143
144 /* skip unused args */
145 if (isArgUnused) {
146 continue;
147 }
148
149 if (ploc.reg0 != x64::kRinvalid) {
150 /* load to the register. */
151 RegOperand &parmRegOpnd = cgFunc->GetOpndBuilder()->CreatePReg(ploc.reg0, GetPrimTypeBitSize(primType),
152 cgFunc->GetRegTyFromPrimTy(primType));
153 paramPassByReg.push_back({&parmRegOpnd, argOpnd, primType});
154 if (x64::IsFPSIMDRegister(static_cast<X64reg>(ploc.reg0))) {
155 fpNum++;
156 }
157 } else {
158 /* load to stack memory */
159 RegOperand &baseOpnd =
160 cgFunc->GetOpndBuilder()->CreatePReg(x64::RSP, k64BitSize, cgFunc->GetRegTyFromPrimTy(primType));
161 MemOperand &actMemOpnd =
162 cgFunc->GetOpndBuilder()->CreateMem(baseOpnd, ploc.memOffset, GetPrimTypeBitSize(primType));
163 SelectCopy(actMemOpnd, *argOpnd, primType);
164 }
165 DEBUG_ASSERT(ploc.reg1 == 0, "SelectCall NIY");
166 }
167
168 /* param pass by reg */
169 for (auto [regOpnd, argOpnd, primType] : paramPassByReg) {
170 DEBUG_ASSERT(regOpnd != nullptr, "not null check");
171 DEBUG_ASSERT(argOpnd != nullptr, "not null check");
172 SelectCopy(*regOpnd, *argOpnd, primType);
173 srcOpnds.PushOpnd(*regOpnd);
174 }
175 }
176
SelectSpecialRegread(PregIdx pregIdx,PrimType primType)177 RegOperand &X64MPIsel::SelectSpecialRegread(PregIdx pregIdx, PrimType primType)
178 {
179 switch (-pregIdx) {
180 case kSregFp: {
181 return cgFunc->GetOpndBuilder()->CreatePReg(x64::RFP, k64BitSize, cgFunc->GetRegTyFromPrimTy(primType));
182 }
183 case kSregSp: {
184 return cgFunc->GetOpndBuilder()->CreatePReg(x64::RSP, k64BitSize, cgFunc->GetRegTyFromPrimTy(primType));
185 }
186 default: {
187 CHECK_FATAL(false, "ERROR: Not supported special register!");
188 }
189 }
190 }
191
SelectFloatingConst(MIRConst & floatingConst,PrimType primType) const192 Operand *X64MPIsel::SelectFloatingConst(MIRConst &floatingConst, PrimType primType) const
193 {
194 CHECK_FATAL(primType == PTY_f64 || primType == PTY_f32, "wrong const");
195 uint32 labelIdxTmp = cgFunc->GetLabelIdx();
196 Operand *result = nullptr;
197 if (primType == PTY_f64) {
198 result = SelectLiteral(static_cast<MIRDoubleConst &>(floatingConst), cgFunc->GetFunction(), labelIdxTmp++);
199 } else {
200 result = SelectLiteral(static_cast<MIRFloatConst &>(floatingConst), cgFunc->GetFunction(), labelIdxTmp++);
201 }
202 cgFunc->SetLabelIdx(labelIdxTmp);
203 return result;
204 }
205
AppendCall(x64::X64MOP_t mOp,Operand & targetOpnd,ListOperand & paramOpnds,ListOperand & retOpnds)206 Insn &X64MPIsel::AppendCall(x64::X64MOP_t mOp, Operand &targetOpnd, ListOperand ¶mOpnds, ListOperand &retOpnds)
207 {
208 Insn &callInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
209 callInsn.AddOpndChain(targetOpnd).AddOpndChain(paramOpnds).AddOpndChain(retOpnds);
210 cgFunc->GetCurBB()->AppendInsn(callInsn);
211 cgFunc->GetCurBB()->SetHasCall();
212 cgFunc->GetFunction().SetHasCall();
213 return callInsn;
214 }
215
SelectCalleeReturn(MIRType * retType,ListOperand & retOpnds)216 void X64MPIsel::SelectCalleeReturn(MIRType *retType, ListOperand &retOpnds)
217 {
218 if (retType == nullptr) {
219 return;
220 }
221 auto retSize = retType->GetSize() * kBitsPerByte;
222 if (retSize <= k128BitSize) {
223 if (retSize > k0BitSize) {
224 retOpnds.PushOpnd(cgFunc->GetOpndBuilder()->CreatePReg(x64::RAX, k64BitSize, kRegTyInt));
225 }
226 if (retSize > k64BitSize) {
227 retOpnds.PushOpnd(cgFunc->GetOpndBuilder()->CreatePReg(x64::RDX, k64BitSize, kRegTyInt));
228 }
229 }
230 }
231
SelectCall(CallNode & callNode)232 void X64MPIsel::SelectCall(CallNode &callNode)
233 {
234 MIRFunction *fn = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode.GetPUIdx());
235 MIRSymbol *fsym = GlobalTables::GetGsymTable().GetSymbolFromStidx(fn->GetStIdx().Idx(), false);
236 Operand &targetOpnd = cgFunc->GetOpndBuilder()->CreateFuncNameOpnd(*fsym);
237
238 ListOperand ¶mOpnds = cgFunc->GetOpndBuilder()->CreateList();
239 uint32 fpNum = 0;
240 SelectParmList(callNode, paramOpnds, fpNum);
241 /* x64abi: rax = with variable arguments passes information about the number of vector registers used */
242 if (fn->IsVarargs()) {
243 ImmOperand &fpNumImm = cgFunc->GetOpndBuilder()->CreateImm(k64BitSize, fpNum);
244 RegOperand &raxOpnd = cgFunc->GetOpndBuilder()->CreatePReg(x64::RAX, k64BitSize, kRegTyInt);
245 SelectCopy(raxOpnd, fpNumImm, PTY_i64);
246 }
247
248 MIRType *retType = fn->GetReturnType();
249 ListOperand &retOpnds = cgFunc->GetOpndBuilder()->CreateList();
250 SelectCalleeReturn(retType, retOpnds);
251
252 Insn &callInsn = AppendCall(x64::MOP_callq_l, targetOpnd, paramOpnds, retOpnds);
253 if (retType != nullptr) {
254 callInsn.SetRetSize(static_cast<uint32>(retType->GetSize()));
255 callInsn.SetIsCallReturnUnsigned(IsUnsignedInteger(retType->GetPrimType()));
256 }
257 const auto &deoptBundleInfo = callNode.GetDeoptBundleInfo();
258 for (const auto &elem : deoptBundleInfo) {
259 auto valueKind = elem.second.GetMapleValueKind();
260 if (valueKind == MapleValue::kPregKind) {
261 auto *opnd = cgFunc->GetOrCreateRegOpndFromPregIdx(elem.second.GetPregIdx(), PTY_ref);
262 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
263 } else if (valueKind == MapleValue::kConstKind) {
264 auto *opnd = SelectIntConst(static_cast<const MIRIntConst &>(elem.second.GetConstValue()), PTY_i32);
265 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
266 } else {
267 CHECK_FATAL(false, "not supported currently");
268 }
269 }
270 cgFunc->AppendStackMapInsn(callInsn);
271 }
272
SelectIcall(IcallNode & iCallNode)273 void X64MPIsel::SelectIcall(IcallNode &iCallNode)
274 {
275 Operand *opnd0 = HandleExpr(iCallNode, *iCallNode.GetNopndAt(0));
276 RegOperand &targetOpnd = SelectCopy2Reg(*opnd0, iCallNode.Opnd(0)->GetPrimType());
277 ListOperand ¶mOpnds = cgFunc->GetOpndBuilder()->CreateList();
278 uint32 fpNum = 0;
279 SelectParmList(iCallNode, paramOpnds, fpNum);
280
281 MIRType *retType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(iCallNode.GetRetTyIdx());
282 if (iCallNode.GetOpCode() == OP_icallproto) {
283 CHECK_FATAL((retType->GetKind() == kTypeFunction), "NIY, must be func");
284 auto calleeType = static_cast<MIRFuncType *>(retType);
285 retType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(calleeType->GetRetTyIdx());
286 }
287 ListOperand &retOpnds = cgFunc->GetOpndBuilder()->CreateList();
288 SelectCalleeReturn(retType, retOpnds);
289
290 Insn &callInsn = AppendCall(x64::MOP_callq_r, targetOpnd, paramOpnds, retOpnds);
291 if (retType != nullptr) {
292 callInsn.SetRetSize(static_cast<uint32>(retType->GetSize()));
293 callInsn.SetIsCallReturnUnsigned(IsUnsignedInteger(retType->GetPrimType()));
294 }
295 const auto &deoptBundleInfo = iCallNode.GetDeoptBundleInfo();
296 for (const auto &elem : deoptBundleInfo) {
297 auto valueKind = elem.second.GetMapleValueKind();
298 if (valueKind == MapleValue::kPregKind) {
299 auto *opnd = cgFunc->GetOrCreateRegOpndFromPregIdx(elem.second.GetPregIdx(), PTY_ref);
300 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
301 } else if (valueKind == MapleValue::kConstKind) {
302 auto *opnd = SelectIntConst(static_cast<const MIRIntConst &>(elem.second.GetConstValue()), PTY_i32);
303 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
304 } else {
305 CHECK_FATAL(false, "not supported currently");
306 }
307 }
308 cgFunc->AppendStackMapInsn(callInsn);
309 }
310
ProcessReturnReg(PrimType primType,int32 sReg)311 Operand &X64MPIsel::ProcessReturnReg(PrimType primType, int32 sReg)
312 {
313 return GetTargetRetOperand(primType, sReg);
314 }
315
SelectGoto(GotoNode & stmt)316 void X64MPIsel::SelectGoto(GotoNode &stmt)
317 {
318 MOperator mOp = x64::MOP_jmpq_l;
319 auto funcName = ".L." + std::to_string(cgFunc->GetUniqueID()) + "__" + std::to_string(stmt.GetOffset());
320 LabelOperand &targetOpnd = cgFunc->GetOpndBuilder()->CreateLabel(funcName.c_str(), stmt.GetOffset());
321 Insn &jmpInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
322 cgFunc->GetCurBB()->AppendInsn(jmpInsn);
323 jmpInsn.AddOpndChain(targetOpnd);
324 cgFunc->GetCurBB()->SetKind(BB::kBBGoto);
325 return;
326 }
327
SelectOverFlowCall(const IntrinsiccallNode & intrnNode)328 void X64MPIsel::SelectOverFlowCall(const IntrinsiccallNode &intrnNode)
329 {
330 DEBUG_ASSERT(intrnNode.NumOpnds() == kOpndNum2, "must be 2 operands");
331 MIRIntrinsicID intrinsic = intrnNode.GetIntrinsic();
332 // add
333 PrimType type = intrnNode.Opnd(0)->GetPrimType();
334 PrimType type2 = intrnNode.Opnd(1)->GetPrimType();
335 CHECK_FATAL(type == PTY_i32 || type == PTY_u32, "only support i32 or u32 here");
336 CHECK_FATAL(type2 == PTY_i32 || type2 == PTY_u32, "only support i32 or u32 here");
337 RegOperand &opnd0 = SelectCopy2Reg(*HandleExpr(intrnNode, *intrnNode.Opnd(0)),
338 intrnNode.Opnd(0)->GetPrimType()); /* first argument of intrinsic */
339 RegOperand &opnd1 = SelectCopy2Reg(*HandleExpr(intrnNode, *intrnNode.Opnd(1)),
340 intrnNode.Opnd(1)->GetPrimType()); /* first argument of intrinsic */
341 RegOperand &resReg =
342 cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(type), cgFunc->GetRegTyFromPrimTy(type));
343 if (intrinsic == INTRN_ADD_WITH_OVERFLOW) {
344 SelectAdd(resReg, opnd0, opnd1, type);
345 } else if (intrinsic == INTRN_SUB_WITH_OVERFLOW) {
346 SelectSub(resReg, opnd0, opnd1, type);
347 } else if (intrinsic == INTRN_MUL_WITH_OVERFLOW) {
348 SelectMpy(resReg, opnd0, opnd1, type);
349 } else {
350 CHECK_FATAL(false, "niy");
351 }
352
353 // store
354 auto *p2nrets = &intrnNode.GetReturnVec();
355 if (p2nrets->size() == k2ByteSize) {
356 CHECK_NULL_FATAL(cgFunc->GetBecommon().GetMIRModule().CurFunction());
357 PregIdx pregIdx = (*p2nrets)[0].second.GetPregIdx();
358 MIRPreg *mirPreg = cgFunc->GetFunction().GetPregTab()->PregFromPregIdx(pregIdx);
359 PrimType regType = mirPreg->GetPrimType();
360 RegOperand &retReg = cgFunc->GetOpndBuilder()->CreateVReg(cgFunc->GetVirtualRegNOFromPseudoRegIdx(pregIdx),
361 GetPrimTypeBitSize(regType), cgFunc->GetRegTyFromPrimTy(regType));
362 SelectCopy(retReg, resReg, type);
363 PregIdx pregIdx2 = (*p2nrets)[1].second.GetPregIdx();
364 MIRPreg *mirPreg2 = cgFunc->GetFunction().GetPregTab()->PregFromPregIdx(pregIdx2);
365 PrimType regType2 = mirPreg2->GetPrimType();
366 RegOperand &retReg2 = cgFunc->GetOpndBuilder()->CreateVReg(cgFunc->GetVirtualRegNOFromPseudoRegIdx(pregIdx2),
367 GetPrimTypeBitSize(regType2), cgFunc->GetRegTyFromPrimTy(regType2));
368 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(MOP_seto_r, X64CG::kMd[MOP_seto_r]);
369 insn.AddOpndChain(retReg2);
370 cgFunc->GetCurBB()->AppendInsn(insn);
371 } else {
372 CHECK_FATAL(false, "should not happen");
373 }
374 return;
375 }
376
SelectPureCall(const IntrinsiccallNode & intrnNode)377 void X64MPIsel::SelectPureCall(const IntrinsiccallNode &intrnNode)
378 {
379 DEBUG_ASSERT(intrnNode.NumOpnds() == 6, "must be 6 operands"); // must be 6 operands
380 ListOperand &srcOpnds = cgFunc->GetOpndBuilder()->CreateList();
381 auto &callee = *intrnNode.Opnd(0);
382 auto ptyp = callee.GetPrimType();
383 RegOperand &calleeReg = SelectCopy2Reg(*HandleExpr(intrnNode, callee), ptyp);
384 uint32 i = 1;
385 for (; i < kSeventhReg; i++) {
386 srcOpnds.PushOpnd(LoadOpndIntoPhysicalRegister(intrnNode, i));
387 }
388 // R11 is used in asm call
389 srcOpnds.PushOpnd(cgFunc->GetOpndBuilder()->CreatePReg(x64::R11, GetPrimTypeBitSize(PTY_i64), kRegTyInt));
390 MOperator mOp = x64::MOP_pure_call;
391 Insn &callInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
392 callInsn.AddOpndChain(calleeReg);
393 callInsn.AddOpndChain(srcOpnds);
394 cgFunc->GetCurBB()->AppendInsn(callInsn);
395 return;
396 }
397
LoadOpndIntoPhysicalRegister(const IntrinsiccallNode & intrnNode,uint32 index)398 RegOperand &X64MPIsel::LoadOpndIntoPhysicalRegister(const IntrinsiccallNode &intrnNode, uint32 index)
399 {
400 auto &opnd = *intrnNode.Opnd(index);
401 auto ptyp = opnd.GetPrimType();
402 RegOperand &opndReg = SelectCopy2Reg(*HandleExpr(intrnNode, opnd), ptyp);
403 PRegNo regId;
404 switch (index - 1) {
405 case kFirstReg:
406 regId = x64::RDI;
407 break;
408 case kSecondReg:
409 regId = x64::RSI;
410 break;
411 case kThirdReg:
412 regId = x64::RDX;
413 break;
414 case kFourthReg:
415 regId = x64::RCX;
416 break;
417 case kFifthReg:
418 regId = x64::R8;
419 break;
420 case kSixthReg:
421 regId = x64::R9;
422 break;
423 default:
424 CHECK_FATAL_FALSE("Unreachable!");
425 }
426 RegOperand &realReg = cgFunc->GetOpndBuilder()->CreatePReg(regId, GetPrimTypeBitSize(PTY_i64), kRegTyInt);
427 SelectCopy(realReg, opndReg, ptyp, ptyp);
428 return realReg;
429 }
430
SelectIntrinsicCall(IntrinsiccallNode & intrinsiccallNode)431 void X64MPIsel::SelectIntrinsicCall(IntrinsiccallNode &intrinsiccallNode)
432 {
433 MIRIntrinsicID intrinsic = intrinsiccallNode.GetIntrinsic();
434 if (intrinsic == INTRN_ADD_WITH_OVERFLOW || intrinsic == INTRN_SUB_WITH_OVERFLOW ||
435 intrinsic == INTRN_MUL_WITH_OVERFLOW) {
436 SelectOverFlowCall(intrinsiccallNode);
437 return;
438 }
439 if (intrinsic == maple::INTRN_JS_PURE_CALL) {
440 SelectPureCall(intrinsiccallNode);
441 return;
442 }
443
444 CHECK_FATAL(false, "Intrinsic %d: %s not implemented by the X64 CG.", intrinsic, GetIntrinsicName(intrinsic));
445 }
446
SelectDeoptCall(CallNode & callNode)447 void X64MPIsel::SelectDeoptCall(CallNode &callNode)
448 {
449 MIRFunction *fn = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode.GetPUIdx());
450 MIRSymbol *fsym = GlobalTables::GetGsymTable().GetSymbolFromStidx(fn->GetStIdx().Idx(), false);
451 Operand &targetOpnd = cgFunc->GetOpndBuilder()->CreateFuncNameOpnd(*fsym);
452
453 ListOperand ¶mOpnds = cgFunc->GetOpndBuilder()->CreateList();
454 uint32 fpNum = 0;
455 SelectParmList(callNode, paramOpnds, fpNum);
456 /* x64abi: rax = with variable arguments passes information about the number of vector registers used */
457 if (fn->IsVarargs()) {
458 ImmOperand &fpNumImm = cgFunc->GetOpndBuilder()->CreateImm(k64BitSize, fpNum);
459 RegOperand &raxOpnd = cgFunc->GetOpndBuilder()->CreatePReg(x64::RAX, k64BitSize, kRegTyInt);
460 SelectCopy(raxOpnd, fpNumImm, PTY_i64);
461 }
462 ListOperand &retOpnds = cgFunc->GetOpndBuilder()->CreateList();
463 Insn &callInsn = AppendCall(x64::MOP_callq_l, targetOpnd, paramOpnds, retOpnds);
464 const auto &deoptBundleInfo = callNode.GetDeoptBundleInfo();
465 for (const auto &elem : deoptBundleInfo) {
466 auto valueKind = elem.second.GetMapleValueKind();
467 if (valueKind == MapleValue::kPregKind) {
468 auto *opnd = cgFunc->GetOrCreateRegOpndFromPregIdx(elem.second.GetPregIdx(), PTY_ref);
469 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
470 } else if (valueKind == MapleValue::kConstKind) {
471 auto *opnd = SelectIntConst(static_cast<const MIRIntConst &>(elem.second.GetConstValue()), PTY_i32);
472 callInsn.AddDeoptBundleInfo(elem.first, *opnd);
473 } else {
474 CHECK_FATAL(false, "not supported currently");
475 }
476 }
477 cgFunc->AppendStackMapInsn(callInsn);
478 }
479
SelectTailICall(IcallNode & icallNode)480 void X64MPIsel::SelectTailICall(IcallNode &icallNode)
481 {
482 Operand *opnd0 = HandleExpr(icallNode, *icallNode.GetNopndAt(0));
483 RegOperand &targetOpnd = SelectCopy2Reg(*opnd0, icallNode.Opnd(0)->GetPrimType());
484 ListOperand ¶mOpnds = cgFunc->GetOpndBuilder()->CreateList();
485 uint32 fpNum = 0;
486 SelectParmList(icallNode, paramOpnds, fpNum);
487 Insn &callInsn = cgFunc->GetInsnBuilder()->BuildInsn(x64::MOP_tail_callq_r, targetOpnd, paramOpnds);
488 cgFunc->GetCurBB()->AppendInsn(callInsn);
489 }
490
SelectRangeGoto(RangeGotoNode & rangeGotoNode,Operand & srcOpnd)491 void X64MPIsel::SelectRangeGoto(RangeGotoNode &rangeGotoNode, Operand &srcOpnd)
492 {
493 MIRType *etype = GlobalTables::GetTypeTable().GetTypeFromTyIdx((TyIdx)PTY_a64);
494 std::vector<uint64> sizeArray;
495 const SmallCaseVector &switchTable = rangeGotoNode.GetRangeGotoTable();
496 sizeArray.emplace_back(switchTable.size());
497 MemPool *memPool = cgFunc->GetMemoryPool();
498 MIRArrayType *arrayType = memPool->New<MIRArrayType>(etype->GetTypeIndex(), sizeArray);
499 MIRAggConst *arrayConst = memPool->New<MIRAggConst>(cgFunc->GetMirModule(), *arrayType);
500 for (const auto &itPair : switchTable) {
501 LabelIdx labelIdx = itPair.second;
502 cgFunc->GetCurBB()->PushBackRangeGotoLabel(labelIdx);
503 MIRConst *mirConst = memPool->New<MIRLblConst>(labelIdx, cgFunc->GetFunction().GetPuidx(), *etype);
504 arrayConst->AddItem(mirConst, 0);
505 }
506 MIRSymbol *lblSt = cgFunc->GetFunction().GetSymTab()->CreateSymbol(kScopeLocal);
507 lblSt->SetStorageClass(kScFstatic);
508 lblSt->SetSKind(kStConst);
509 lblSt->SetTyIdx(arrayType->GetTypeIndex());
510 lblSt->SetKonst(arrayConst);
511 std::string lblStr(".L_");
512 uint32 labelIdxTmp = cgFunc->GetLabelIdx();
513 lblStr.append(std::to_string(cgFunc->GetUniqueID())).append("_LOCAL_CONST.").append(std::to_string(labelIdxTmp++));
514 cgFunc->SetLabelIdx(labelIdxTmp);
515 lblSt->SetNameStrIdx(lblStr);
516 cgFunc->AddEmitSt(cgFunc->GetCurBB()->GetId(), *lblSt);
517 ImmOperand &stOpnd = cgFunc->GetOpndBuilder()->CreateImm(*lblSt, 0, 0);
518 /* get index */
519 PrimType srcType = rangeGotoNode.Opnd(0)->GetPrimType();
520 RegOperand &opnd0 = SelectCopy2Reg(srcOpnd, srcType);
521 int32 minIdx = switchTable[0].first;
522 ImmOperand &opnd1 =
523 cgFunc->GetOpndBuilder()->CreateImm(GetPrimTypeBitSize(srcType), -minIdx - rangeGotoNode.GetTagOffset());
524 RegOperand &indexOpnd = cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(srcType), kRegTyInt);
525 SelectAdd(indexOpnd, opnd0, opnd1, srcType);
526
527 /* load the displacement into a register by accessing memory at base + index * 8 */
528 /* mov .L_xxx_LOCAL_CONST.x(%baseReg, %indexOpnd, 8), %dstRegOpnd */
529 MemOperand &dstMemOpnd = cgFunc->GetOpndBuilder()->CreateMem(GetPrimTypeBitSize(PTY_a64));
530 RegOperand &baseReg = cgFunc->GetOpndBuilder()->CreatePReg(x64::RBP, GetPrimTypeBitSize(PTY_i64), kRegTyInt);
531 dstMemOpnd.SetBaseRegister(baseReg);
532 dstMemOpnd.SetIndexRegister(indexOpnd);
533 dstMemOpnd.SetOffsetOperand(stOpnd);
534 dstMemOpnd.SetScaleOperand(cgFunc->GetOpndBuilder()->CreateImm(baseReg.GetSize(), k8ByteSize));
535
536 /* jumping to the absolute address which is stored in dstRegOpnd */
537 MOperator mOp = x64::MOP_jmpq_m;
538 Insn &jmpInsn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
539 jmpInsn.AddOpndChain(dstMemOpnd);
540 cgFunc->GetCurBB()->AppendInsn(jmpInsn);
541 }
542
543 /*
544 * unorded ZF, PF, CF ==> 1,1,1
545 * above ZF, PF, CF ==> 0,0,0
546 * below ZF, PF, CF ==> 0,0,1
547 * equal ZF, PF, CF ==> 1,0,0
548 *
549 * To distinguish between less than(only check whether CF = 1 or not) and unorderd(CF=1),
550 * So ** judging gt/ge by swaping operands is used to represent lt/le in float**
551 */
PickJmpInsn(Opcode brOp,Opcode cmpOp,bool isFloat,bool isSigned)552 static X64MOP_t PickJmpInsn(Opcode brOp, Opcode cmpOp, bool isFloat, bool isSigned)
553 {
554 switch (cmpOp) {
555 case OP_ne:
556 return (brOp == OP_brtrue) ? MOP_jne_l : MOP_je_l;
557 case OP_eq:
558 return (brOp == OP_brtrue) ? MOP_je_l : MOP_jne_l;
559 case OP_lt:
560 return (brOp == OP_brtrue) ? (isFloat ? MOP_ja_l : (isSigned ? MOP_jl_l : MOP_jb_l))
561 : (isSigned ? MOP_jge_l : MOP_jae_l);
562 case OP_le:
563 return (brOp == OP_brtrue) ? (isFloat ? MOP_jae_l : (isSigned ? MOP_jle_l : MOP_jbe_l))
564 : (isSigned ? MOP_jg_l : MOP_ja_l);
565 case OP_gt:
566 return (brOp == OP_brtrue) ? (isFloat ? MOP_ja_l : (isSigned ? MOP_jg_l : MOP_ja_l))
567 : (isSigned ? MOP_jle_l : MOP_jbe_l);
568 case OP_ge:
569 return (brOp == OP_brtrue) ? (isSigned ? MOP_jge_l : MOP_jae_l) : (isSigned ? MOP_jl_l : MOP_jb_l);
570 default:
571 CHECK_FATAL(false, "PickJmpInsn error");
572 }
573 }
574
575 /*
576 * handle brfalse/brtrue op, opnd0 can be a compare node or non-compare node
577 * such as a dread for example
578 */
SelectCondGoto(CondGotoNode & stmt,BaseNode & condNode,Operand & opnd0)579 void X64MPIsel::SelectCondGoto(CondGotoNode &stmt, BaseNode &condNode, Operand &opnd0)
580 {
581 Opcode opcode = stmt.GetOpCode();
582 X64MOP_t jmpOperator = x64::MOP_begin;
583 if (opnd0.IsImmediate()) {
584 DEBUG_ASSERT(opnd0.IsIntImmediate(), "only support int immediate");
585 DEBUG_ASSERT(opcode == OP_brtrue || opcode == OP_brfalse, "unsupported opcode");
586 ImmOperand &immOpnd0 = static_cast<ImmOperand &>(opnd0);
587 if ((opcode == OP_brtrue && !(immOpnd0.GetValue() != 0)) ||
588 (opcode == OP_brfalse && !(immOpnd0.GetValue() == 0))) {
589 return;
590 }
591 jmpOperator = x64::MOP_jmpq_l;
592 cgFunc->SetCurBBKind(BB::kBBGoto);
593 } else {
594 PrimType primType;
595 Opcode condOpcode = condNode.GetOpCode();
596 // op_ne
597 if (!kOpcodeInfo.IsCompare(condOpcode)) {
598 primType = condNode.GetPrimType();
599 ImmOperand &imm0 = cgFunc->GetOpndBuilder()->CreateImm(GetPrimTypeBitSize(primType), 0);
600 SelectCmp(opnd0, imm0, primType);
601 condOpcode = OP_ne;
602 } else {
603 primType = static_cast<CompareNode &>(condNode).GetOpndType();
604 }
605 bool isFloat = IsPrimitiveFloat(primType);
606 jmpOperator = PickJmpInsn(opcode, condOpcode, isFloat, IsSignedInteger(primType));
607 cgFunc->SetCurBBKind(BB::kBBIf);
608 }
609 /* gen targetOpnd, .L.xxx__xx */
610 auto funcName = ".L." + std::to_string(cgFunc->GetUniqueID()) + "__" + std::to_string(stmt.GetOffset());
611 LabelOperand &targetOpnd = cgFunc->GetOpndBuilder()->CreateLabel(funcName.c_str(), stmt.GetOffset());
612 /* select jump Insn */
613 Insn &jmpInsn = (cgFunc->GetInsnBuilder()->BuildInsn(jmpOperator, X64CG::kMd[jmpOperator]));
614 jmpInsn.AddOpndChain(targetOpnd);
615 cgFunc->GetCurBB()->AppendInsn(jmpInsn);
616 }
617
GetTargetRetOperand(PrimType primType,int32 sReg)618 Operand &X64MPIsel::GetTargetRetOperand(PrimType primType, int32 sReg)
619 {
620 uint32 bitSize = GetPrimTypeBitSize(primType);
621 regno_t retReg = 0;
622 switch (sReg) {
623 case kSregRetval0:
624 retReg = IsPrimitiveFloat(primType) ? x64::V0 : x64::RAX;
625 break;
626 case kSregRetval1:
627 retReg = x64::RDX;
628 break;
629 default:
630 CHECK_FATAL(false, "GetTargetRetOperand: NIY");
631 break;
632 }
633 RegOperand &parmRegOpnd =
634 cgFunc->GetOpndBuilder()->CreatePReg(retReg, bitSize, cgFunc->GetRegTyFromPrimTy(primType));
635 return parmRegOpnd;
636 }
637
SelectMpy(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)638 Operand *X64MPIsel::SelectMpy(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
639 {
640 PrimType dtype = node.GetPrimType();
641 RegOperand *resOpnd = nullptr;
642 resOpnd = &cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(dtype), cgFunc->GetRegTyFromPrimTy(dtype));
643 RegOperand ®Opnd0 = SelectCopy2Reg(opnd0, dtype, node.Opnd(0)->GetPrimType());
644 RegOperand ®Opnd1 = SelectCopy2Reg(opnd1, dtype, node.Opnd(1)->GetPrimType());
645 SelectMpy(*resOpnd, regOpnd0, regOpnd1, dtype);
646 return resOpnd;
647 }
648
SelectMpy(Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)649 void X64MPIsel::SelectMpy(Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
650 {
651 uint32 bitSize = GetPrimTypeBitSize(primType);
652 SelectCopy(resOpnd, opnd0, primType);
653 RegOperand ®Opnd1 = SelectCopy2Reg(opnd1, primType);
654 if (IsSignedInteger(primType) || IsUnsignedInteger(primType)) {
655 X64MOP_t mOp = (bitSize == k64BitSize) ? x64::MOP_imulq_r_r
656 : (bitSize == k32BitSize) ? x64::MOP_imull_r_r
657 : (bitSize == k16BitSize) ? x64::MOP_imulw_r_r
658 : x64::MOP_begin;
659 CHECK_FATAL(mOp != x64::MOP_begin, "NIY mapping");
660 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
661 insn.AddOpndChain(regOpnd1).AddOpndChain(resOpnd);
662 cgFunc->GetCurBB()->AppendInsn(insn);
663 } else if (IsPrimitiveFloat(primType)) {
664 X64MOP_t mOp = (bitSize == k64BitSize) ? x64::MOP_mulfd_r_r
665 : (bitSize == k32BitSize) ? x64::MOP_mulfs_r_r
666 : x64::MOP_begin;
667 CHECK_FATAL(mOp != x64::MOP_begin, "NIY mapping");
668 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
669 insn.AddOpndChain(regOpnd1).AddOpndChain(resOpnd);
670 cgFunc->GetCurBB()->AppendInsn(insn);
671 }
672 }
673
674 /*
675 * Dividend(EDX:EAX) / Divisor(reg/mem32) = Quotient(EAX) Remainder(EDX)
676 * IDIV instruction perform signed division of EDX:EAX by the contents of 32-bit register or memory location and
677 * store the quotient in EAX and the remainder in EDX.
678 * The instruction truncates non-integral results towards 0. The sign of the remainder is always the same as the sign
679 * of the dividend, and the absolute value of the remainder is less than the absolute value of the divisor.
680 * An overflow generates a #DE (divide error) exception, rather than setting the OF flag.
681 * To avoid overflow problems, precede this instruction with a CDQ instruction to sign-extend the dividend Divisor.
682 * CDQ Sign-extend EAX into EDX:EAX. This action helps avoid overflow problems in signed number arithmetic.
683 */
SelectDiv(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)684 Operand *X64MPIsel::SelectDiv(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
685 {
686 PrimType primType = node.GetPrimType();
687 Operand *resOpnd = nullptr;
688 RegOperand ®Opnd0 = SelectCopy2Reg(opnd0, primType, node.Opnd(0)->GetPrimType());
689 RegOperand ®Opnd1 = SelectCopy2Reg(opnd1, primType, node.Opnd(1)->GetPrimType());
690 resOpnd = SelectDivRem(regOpnd0, regOpnd1, primType, node.GetOpCode());
691 return resOpnd;
692 }
693
SelectRem(BinaryNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)694 Operand *X64MPIsel::SelectRem(BinaryNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
695 {
696 PrimType primType = node.GetPrimType();
697 Operand *resOpnd = nullptr;
698 RegOperand ®Opnd0 = SelectCopy2Reg(opnd0, primType, node.Opnd(0)->GetPrimType());
699 RegOperand ®Opnd1 = SelectCopy2Reg(opnd1, primType, node.Opnd(1)->GetPrimType());
700 resOpnd = SelectDivRem(regOpnd0, regOpnd1, primType, node.GetOpCode());
701 return resOpnd;
702 }
703
SelectDivRem(RegOperand & opnd0,RegOperand & opnd1,PrimType primType,Opcode opcode)704 Operand *X64MPIsel::SelectDivRem(RegOperand &opnd0, RegOperand &opnd1, PrimType primType, Opcode opcode)
705 {
706 DEBUG_ASSERT(opcode == OP_div || opcode == OP_rem, "unsupported opcode");
707 if (IsSignedInteger(primType) || IsUnsignedInteger(primType)) {
708 uint32 bitSize = GetPrimTypeBitSize(primType);
709 /* copy dividend to eax */
710 RegOperand &raxOpnd =
711 cgFunc->GetOpndBuilder()->CreatePReg(x64::RAX, bitSize, cgFunc->GetRegTyFromPrimTy(primType));
712 SelectCopy(raxOpnd, opnd0, primType);
713
714 RegOperand &rdxOpnd =
715 cgFunc->GetOpndBuilder()->CreatePReg(x64::RDX, bitSize, cgFunc->GetRegTyFromPrimTy(primType));
716 bool isSigned = IsSignedInteger(primType);
717 if (isSigned) {
718 /* cdq edx:eax = sign-extend of eax*/
719 X64MOP_t cvtMOp = (bitSize == k64BitSize) ? x64::MOP_cqo
720 : (bitSize == k32BitSize) ? x64::MOP_cdq
721 : (bitSize == k16BitSize) ? x64::MOP_cwd
722 : x64::MOP_begin;
723 CHECK_FATAL(cvtMOp != x64::MOP_begin, "NIY mapping");
724 Insn &cvtInsn = cgFunc->GetInsnBuilder()->BuildInsn(cvtMOp, raxOpnd, rdxOpnd);
725 cgFunc->GetCurBB()->AppendInsn(cvtInsn);
726 } else {
727 /* set edx = 0 */
728 SelectCopy(rdxOpnd, cgFunc->GetOpndBuilder()->CreateImm(bitSize, 0), primType);
729 }
730 /* div */
731 X64MOP_t divMOp = (bitSize == k64BitSize) ? (isSigned ? x64::MOP_idivq_r : x64::MOP_divq_r)
732 : (bitSize == k32BitSize) ? (isSigned ? x64::MOP_idivl_r : x64::MOP_divl_r)
733 : (bitSize == k16BitSize) ? (isSigned ? x64::MOP_idivw_r : x64::MOP_divw_r)
734 : x64::MOP_begin;
735 CHECK_FATAL(divMOp != x64::MOP_begin, "NIY mapping");
736 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(divMOp, opnd1, raxOpnd, rdxOpnd);
737 cgFunc->GetCurBB()->AppendInsn(insn);
738 /* return */
739 RegOperand &resOpnd = cgFunc->GetOpndBuilder()->CreateVReg(bitSize, cgFunc->GetRegTyFromPrimTy(primType));
740 SelectCopy(resOpnd, ((opcode == OP_div) ? raxOpnd : rdxOpnd), primType);
741 return &resOpnd;
742 } else if (IsPrimitiveFloat(primType)) {
743 uint32 bitSize = GetPrimTypeBitSize(primType);
744 auto &resOpnd = cgFunc->GetOpndBuilder()->CreateVReg(bitSize, cgFunc->GetRegTyFromPrimTy(primType));
745 SelectCopy(resOpnd, opnd0, primType);
746 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(x64::MOP_divsd_r, opnd1, resOpnd);
747 cgFunc->GetCurBB()->AppendInsn(insn);
748 return &resOpnd;
749 } else {
750 CHECK_FATAL(false, "NIY");
751 }
752 }
753
SelectLnot(const UnaryNode & node,Operand & opnd0,const BaseNode & parent)754 Operand *X64MPIsel::SelectLnot(const UnaryNode &node, Operand &opnd0, const BaseNode &parent)
755 {
756 PrimType dtype = node.GetPrimType();
757 RegOperand *resOpnd = nullptr;
758 resOpnd = &cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(dtype), cgFunc->GetRegTyFromPrimTy(dtype));
759 RegOperand ®Opnd0 = SelectCopy2Reg(opnd0, dtype, node.Opnd(0)->GetPrimType());
760 ImmOperand &immOpnd = cgFunc->GetOpndBuilder()->CreateImm(GetPrimTypeBitSize(dtype), 0);
761 if (IsPrimitiveFloat(dtype)) {
762 SelectCmpFloatEq(*resOpnd, regOpnd0, immOpnd, dtype, dtype);
763 } else {
764 SelectCmp(regOpnd0, immOpnd, dtype);
765 SelectCmpResult(*resOpnd, OP_eq, dtype, dtype);
766 }
767 return resOpnd;
768 }
769
770 /*
771 * unorded ZF, PF, CF ==> 1,1,1
772 * above ZF, PF, CF ==> 0,0,0
773 * below ZF, PF, CF ==> 0,0,1
774 * equal ZF, PF, CF ==> 1,0,0
775 *
776 * To distinguish between less than(only check whether CF = 1 or not) and unorderd(CF=1),
777 * So ** lt/le in float is replaced by judging gt/ge and swaping operands **
778 *
779 * float eq using cmpeqsd, same with llvm
780 */
SelectCmpOp(CompareNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)781 Operand *X64MPIsel::SelectCmpOp(CompareNode &node, Operand &opnd0, Operand &opnd1, const BaseNode &parent)
782 {
783 PrimType dtype = node.GetPrimType();
784 PrimType primOpndType = node.GetOpndType();
785 RegOperand *resOpnd = nullptr;
786 RegOperand ®Opnd0 = SelectCopy2Reg(opnd0, primOpndType, node.Opnd(0)->GetPrimType());
787 RegOperand ®Opnd1 = SelectCopy2Reg(opnd1, primOpndType, node.Opnd(1)->GetPrimType());
788 resOpnd = &cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(dtype), cgFunc->GetRegTyFromPrimTy(dtype));
789 auto nodeOp = node.GetOpCode();
790 Opcode parentOp = parent.GetOpCode();
791 bool isFloat = IsPrimitiveFloat(primOpndType);
792 bool isJump = (parentOp == OP_brfalse || parentOp == OP_brtrue);
793 // float eq
794 if (isFloat && (nodeOp == maple::OP_eq) && (!isJump)) {
795 SelectCmpFloatEq(*resOpnd, regOpnd0, regOpnd1, dtype, primOpndType);
796 return resOpnd;
797 }
798
799 bool isSwap = (isFloat && (nodeOp == maple::OP_le || nodeOp == maple::OP_lt) && (parentOp != OP_brfalse));
800 SelectCmp(regOpnd0, regOpnd1, primOpndType, isSwap);
801 if (isJump) {
802 return resOpnd;
803 }
804 SelectCmpResult(*resOpnd, nodeOp, dtype, primOpndType);
805 return resOpnd;
806 }
807
SelectCmp(Operand & opnd0,Operand & opnd1,PrimType primType,bool isSwap)808 void X64MPIsel::SelectCmp(Operand &opnd0, Operand &opnd1, PrimType primType, bool isSwap)
809 {
810 x64::X64MOP_t cmpMOp = x64::MOP_begin;
811 if (IsPrimitiveInteger(primType)) {
812 cmpMOp = GetCmpMop(opnd0.GetKind(), opnd1.GetKind(), primType);
813 } else if (IsPrimitiveFloat(primType)) {
814 cmpMOp = x64::MOP_ucomisd_r_r;
815 } else {
816 CHECK_FATAL(false, "NIY");
817 }
818 DEBUG_ASSERT(cmpMOp != x64::MOP_begin, "unsupported mOp");
819 Insn &cmpInsn = (cgFunc->GetInsnBuilder()->BuildInsn(cmpMOp, X64CG::kMd[cmpMOp]));
820 if (isSwap) {
821 cmpInsn.AddOpndChain(opnd0).AddOpndChain(opnd1);
822 } else {
823 cmpInsn.AddOpndChain(opnd1).AddOpndChain(opnd0);
824 }
825 cgFunc->GetCurBB()->AppendInsn(cmpInsn);
826 }
827
SelectCmpFloatEq(RegOperand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primResType,PrimType primOpndType)828 void X64MPIsel::SelectCmpFloatEq(RegOperand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primResType,
829 PrimType primOpndType)
830 {
831 /* float eq using cmpeqsd is same with llvm */
832 x64::X64MOP_t eqMOp = x64::MOP_cmpeqsd_r_r;
833 Insn &setInsn = cgFunc->GetInsnBuilder()->BuildInsn(eqMOp, X64CG::kMd[eqMOp]);
834
835 auto ®Opnd1 = cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(primOpndType),
836 cgFunc->GetRegTyFromPrimTy(primOpndType));
837 SelectCopy(regOpnd1, opnd1, primOpndType);
838 /* CMPEQSD xmm1, xmm2 => CMPSD xmm1, xmm2, 0 */
839 setInsn.AddOpndChain(opnd0).AddOpndChain(regOpnd1);
840 cgFunc->GetCurBB()->AppendInsn(setInsn);
841
842 /* set result -> u64/u32 */
843 auto tmpResType = (primOpndType == maple::PTY_f64) ? PTY_u64 : PTY_u32;
844 RegOperand &tmpResOpnd =
845 cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(tmpResType), cgFunc->GetRegTyFromPrimTy(tmpResType));
846 SelectRetypeFloat(tmpResOpnd, regOpnd1, tmpResType, primOpndType);
847 /* cvt u64/u32 -> primType */
848 SelectIntCvt(resOpnd, tmpResOpnd, primResType, tmpResType);
849 }
850
SelectCmpResult(RegOperand & resOpnd,Opcode opCode,PrimType primType,PrimType primOpndType)851 void X64MPIsel::SelectCmpResult(RegOperand &resOpnd, Opcode opCode, PrimType primType, PrimType primOpndType)
852 {
853 bool isFloat = IsPrimitiveFloat(primOpndType);
854 bool isSigned = (!IsPrimitiveUnsigned(primOpndType) && !IsPrimitiveFloat(primOpndType));
855 /* set result -> u8 */
856 RegOperand &tmpResOpnd = cgFunc->GetOpndBuilder()->CreateVReg(k8BitSize, cgFunc->GetRegTyFromPrimTy(PTY_u8));
857 x64::X64MOP_t setMOp = GetSetCCMop(opCode, tmpResOpnd.GetKind(), isSigned, isFloat);
858 DEBUG_ASSERT(setMOp != x64::MOP_begin, "unsupported mOp");
859 Insn &setInsn = cgFunc->GetInsnBuilder()->BuildInsn(setMOp, X64CG::kMd[setMOp]);
860 setInsn.AddOpndChain(tmpResOpnd);
861 cgFunc->GetCurBB()->AppendInsn(setInsn);
862 /* cvt u8 -> primType */
863 SelectIntCvt(resOpnd, tmpResOpnd, primType, PTY_u8);
864 }
865
SelectSelect(Operand & resOpnd,Operand & trueOpnd,Operand & falseOpnd,PrimType primType,Opcode cmpOpcode,PrimType cmpPrimType)866 void X64MPIsel::SelectSelect(Operand &resOpnd, Operand &trueOpnd, Operand &falseOpnd, PrimType primType,
867 Opcode cmpOpcode, PrimType cmpPrimType)
868 {
869 CHECK_FATAL(!IsPrimitiveFloat(primType), "NIY");
870 bool isSigned = !IsPrimitiveUnsigned(primType);
871 uint32 bitSize = GetPrimTypeBitSize(primType);
872 if (bitSize == k8BitSize) {
873 /* cmov unsupported 8bit, cvt to 32bit */
874 PrimType cvtType = isSigned ? PTY_i32 : PTY_u32;
875 RegOperand &tmpResOpnd = cgFunc->GetOpndBuilder()->CreateVReg(k32BitSize, kRegTyInt);
876 Operand &tmpTrueOpnd = SelectCopy2Reg(trueOpnd, cvtType, primType);
877 Operand &tmpFalseOpnd = SelectCopy2Reg(falseOpnd, cvtType, primType);
878 SelectSelect(tmpResOpnd, tmpTrueOpnd, tmpFalseOpnd, cvtType, cmpOpcode, cmpPrimType);
879 SelectCopy(resOpnd, tmpResOpnd, primType, cvtType);
880 return;
881 }
882 RegOperand &tmpOpnd = SelectCopy2Reg(trueOpnd, primType);
883 SelectCopy(resOpnd, falseOpnd, primType);
884 x64::X64MOP_t cmovMop = GetCMovCCMop(cmpOpcode, bitSize, !IsPrimitiveUnsigned(cmpPrimType));
885 DEBUG_ASSERT(cmovMop != x64::MOP_begin, "unsupported mOp");
886 Insn &comvInsn = cgFunc->GetInsnBuilder()->BuildInsn(cmovMop, X64CG::kMd[cmovMop]);
887 comvInsn.AddOpndChain(tmpOpnd).AddOpndChain(resOpnd);
888 cgFunc->GetCurBB()->AppendInsn(comvInsn);
889 }
890
SelectMinOrMax(bool isMin,Operand & resOpnd,Operand & opnd0,Operand & opnd1,PrimType primType)891 void X64MPIsel::SelectMinOrMax(bool isMin, Operand &resOpnd, Operand &opnd0, Operand &opnd1, PrimType primType)
892 {
893 if (IsPrimitiveInteger(primType)) {
894 SelectCmp(opnd0, opnd1, primType);
895 Opcode cmpOpcode = isMin ? OP_lt : OP_gt;
896 SelectSelect(resOpnd, opnd0, opnd1, primType, cmpOpcode, primType);
897 } else {
898 // float lt/le need to swap operands, and using seta
899 CHECK_FATAL(false, "NIY type max or min");
900 }
901 }
902
SelectCctz(IntrinsicopNode & node,Operand & opnd0,const BaseNode & parent)903 Operand *X64MPIsel::SelectCctz(IntrinsicopNode &node, Operand &opnd0, const BaseNode &parent)
904 {
905 CHECK_FATAL(opnd0.IsImmediate() || opnd0.IsRegister(), "unhandled operand type here!");
906 PrimType origPrimType = node.Opnd(0)->GetPrimType();
907 RegOperand &opnd = SelectCopy2Reg(opnd0, origPrimType);
908
909 MOperator mopBsf = x64::MOP_bsfl_r_r;
910 Insn &bsfInsn = cgFunc->GetInsnBuilder()->BuildInsn(mopBsf, X64CG::kMd[mopBsf]);
911 bsfInsn.AddOpndChain(opnd).AddOpndChain(opnd);
912 cgFunc->GetCurBB()->AppendInsn(bsfInsn);
913
914 PrimType retType = node.GetPrimType();
915 RegOperand &destReg =
916 cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(retType), cgFunc->GetRegTyFromPrimTy(retType));
917 // ctz i32 (u32) => cvt u32 -> i32
918 // ctz i32 (u64) => cvt u64 -> i32
919 SelectIntCvt(destReg, opnd, retType, origPrimType);
920 return &destReg;
921 }
922
SelectCclz(IntrinsicopNode & node,Operand & opnd0,const BaseNode & parent)923 Operand *X64MPIsel::SelectCclz(IntrinsicopNode &node, Operand &opnd0, const BaseNode &parent)
924 {
925 CHECK_FATAL(opnd0.IsImmediate() || opnd0.IsRegister(), "unhandled operand type here!");
926 CHECK_FATAL(node.GetIntrinsic() == INTRN_C_clz32, "only support clz32");
927 PrimType origPrimType = node.Opnd(0)->GetPrimType();
928 RegOperand &opnd = SelectCopy2Reg(opnd0, origPrimType);
929 // bsr opnd tmp2
930 ImmOperand &imm =
931 cgFunc->GetOpndBuilder()->CreateImm(GetPrimTypeBitSize(origPrimType), -1);
932 RegOperand &tmp1 = SelectCopy2Reg(imm, origPrimType);
933 RegOperand &tmp2 =
934 cgFunc->GetOpndBuilder()->CreateVReg(
935 GetPrimTypeBitSize(origPrimType), cgFunc->GetRegTyFromPrimTy(origPrimType));
936 MOperator mopBsr = x64::MOP_bsrl_r_r;
937 Insn &bsrInsn = cgFunc->GetInsnBuilder()->BuildInsn(mopBsr, X64CG::kMd[mopBsr]);
938 bsrInsn.AddOpndChain(opnd).AddOpndChain(tmp2);
939 cgFunc->GetCurBB()->AppendInsn(bsrInsn);
940 // cmove -1, tmp2
941 MOperator mopComv = x64::MOP_cmovel_r_r;
942 Insn &cmovInsn = cgFunc->GetInsnBuilder()->BuildInsn(mopComv, X64CG::kMd[mopComv]);
943 cmovInsn.AddOpndChain(tmp1).AddOpndChain(tmp2);
944 cgFunc->GetCurBB()->AppendInsn(cmovInsn);
945 // neg tmp2
946 MOperator mopNeg = x64::MOP_negl_r;
947 Insn &negInsn = cgFunc->GetInsnBuilder()->BuildInsn(mopNeg, X64CG::kMd[mopNeg]);
948 negInsn.AddOpndChain(tmp2);
949 cgFunc->GetCurBB()->AppendInsn(negInsn);
950 // add res 31 tmp2
951 ImmOperand &imm2 =
952 cgFunc->GetOpndBuilder()->CreateImm(GetPrimTypeBitSize(origPrimType), k32BitSize - 1);
953 RegOperand &tmp3 =
954 cgFunc->GetOpndBuilder()->CreateVReg(
955 GetPrimTypeBitSize(origPrimType), cgFunc->GetRegTyFromPrimTy(origPrimType));
956 SelectAdd(tmp3, imm2, tmp2, origPrimType);
957 PrimType retType = node.GetPrimType();
958 RegOperand &destReg =
959 cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(retType), cgFunc->GetRegTyFromPrimTy(retType));
960 SelectIntCvt(destReg, tmp3, retType, origPrimType);
961 return &destReg;
962 }
SelectHeapConstant(IntrinsicopNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)963 RegOperand &X64MPIsel::SelectHeapConstant(IntrinsicopNode &node, Operand &opnd0,
964 Operand &opnd1, const BaseNode &parent)
965 {
966 PrimType retType = node.GetPrimType();
967 RegOperand &destReg =
968 cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(retType), cgFunc->GetRegTyFromPrimTy(retType));
969 MOperator mOp = x64::MOP_heap_const;
970 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
971 insn.AddOpndChain(destReg).AddOpndChain(opnd0).AddOpndChain(opnd1);
972 cgFunc->GetCurBB()->AppendInsn(insn);
973 return destReg;
974 }
975
SelectTaggedIsHeapObject(IntrinsicopNode & node,Operand & opnd0,Operand & opnd1,const BaseNode & parent)976 RegOperand &X64MPIsel::SelectTaggedIsHeapObject(IntrinsicopNode &node, Operand &opnd0,
977 Operand &opnd1, const BaseNode &parent)
978 {
979 RegOperand &destReg = cgFunc->GetOpndBuilder()->CreateVReg(k8BitSize, kRegTyInt);
980 if (opnd0.IsImmediate()) {
981 uint64 value = static_cast<uint64>(static_cast<ImmOperand &>(opnd0).GetValue());
982 uint64 heapObjectMask = static_cast<uint64_t>(static_cast<ImmOperand&>(opnd1).GetValue());
983 MOperator mOp = x64::MOP_movb_i_r;
984 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
985 int8 retValue = static_cast<int64>(value & heapObjectMask) == 0;
986 ImmOperand &imm0 = cgFunc->GetOpndBuilder()->CreateImm(GetPrimTypeActualBitSize(PTY_i8), retValue);
987 insn.AddOpndChain(imm0).AddOpndChain(destReg);
988 cgFunc->GetCurBB()->AppendInsn(insn);
989 } else {
990 RegOperand &tmpReg = cgFunc->GetOpndBuilder()->CreateVReg(k64BitSize, kRegTyInt);
991 MOperator mopMov = x64::MOP_movabs_i_r;
992 Insn &movInsn = cgFunc->GetInsnBuilder()->BuildInsn(mopMov, X64CG::kMd[mopMov]);
993 movInsn.AddOpndChain(opnd1).AddOpndChain(tmpReg);
994 cgFunc->GetCurBB()->AppendInsn(movInsn);
995 MOperator mOp = x64::MOP_tagged_is_heapobject;
996 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
997 insn.AddOpndChain(destReg).AddOpndChain(tmpReg).AddOpndChain(opnd0);
998 cgFunc->GetCurBB()->AppendInsn(insn);
999 }
1000 return destReg;
1001 }
1002
SelectIsStableElements(IntrinsicopNode & node,Operand & opnd0,Operand & opnd1,Operand & opnd2,const BaseNode & parent)1003 RegOperand &X64MPIsel::SelectIsStableElements(IntrinsicopNode &node, Operand &opnd0, Operand &opnd1,
1004 Operand &opnd2, const BaseNode &parent)
1005 {
1006 RegOperand &destReg = cgFunc->GetOpndBuilder()->CreateVReg(k32BitSize, kRegTyInt);
1007 MOperator mOp = x64::MOP_is_stable_elements;
1008 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
1009 insn.AddOpndChain(destReg).AddOpndChain(opnd0).AddOpndChain(opnd1).AddOpndChain(opnd2);
1010 cgFunc->GetCurBB()->AppendInsn(insn);
1011 return destReg;
1012 }
1013
SelectHasPendingException(IntrinsicopNode & node,Operand & opnd0,Operand & opnd1,Operand & opnd2,const BaseNode & parent)1014 RegOperand &X64MPIsel::SelectHasPendingException(
1015 IntrinsicopNode &node, Operand &opnd0, Operand &opnd1, Operand &opnd2, const BaseNode &parent)
1016 {
1017 RegOperand &destReg = cgFunc->GetOpndBuilder()->CreateVReg(k8BitSize, kRegTyInt);
1018 MOperator mOp = x64::MOP_has_pending_exception;
1019 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
1020 insn.AddOpndChain(destReg).AddOpndChain(opnd0).AddOpndChain(opnd1).AddOpndChain(opnd2);
1021 cgFunc->GetCurBB()->AppendInsn(insn);
1022 return destReg;
1023 }
1024
SelectGetHeapConstantTable(IntrinsicopNode & node,Operand & opnd0,Operand & opnd1,Operand & opnd2,const BaseNode & parent)1025 RegOperand &X64MPIsel::SelectGetHeapConstantTable(IntrinsicopNode &node, Operand &opnd0,
1026 Operand &opnd1, Operand &opnd2, const BaseNode &parent)
1027 {
1028 PrimType retType = node.GetPrimType();
1029 RegOperand &destReg =
1030 cgFunc->GetOpndBuilder()->CreateVReg(GetPrimTypeBitSize(retType), cgFunc->GetRegTyFromPrimTy(retType));
1031 MOperator mOp = x64::MOP_get_heap_const_table;
1032 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
1033 insn.AddOpndChain(destReg).AddOpndChain(opnd0).AddOpndChain(opnd1).AddOpndChain(opnd2);
1034 cgFunc->GetCurBB()->AppendInsn(insn);
1035 return destReg;
1036 }
1037
SelectTaggedObjectIsString(IntrinsicopNode & node,Operand & opnd0,Operand & opnd1,Operand & opnd2,Operand & opnd3,Operand & opnd4,const BaseNode & parent)1038 RegOperand &X64MPIsel::SelectTaggedObjectIsString(IntrinsicopNode &node, Operand &opnd0,
1039 Operand &opnd1, Operand &opnd2, Operand &opnd3, Operand &opnd4, const BaseNode &parent)
1040 {
1041 RegOperand &dstReg = cgFunc->GetOpndBuilder()->CreateVReg(k8BitSize, kRegTyInt);
1042 RegOperand &tmpReg1 = cgFunc->GetOpndBuilder()->CreateVReg(k32BitSize, kRegTyInt);
1043 RegOperand &tmpReg2 = cgFunc->GetOpndBuilder()->CreateVReg(k64BitSize, kRegTyInt);
1044 MOperator mOp = x64::MOP_tagged_object_is_string;
1045 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
1046 insn.AddOpndChain(dstReg).AddOpndChain(opnd0).AddOpndChain(opnd1).AddOpndChain(opnd2).AddOpndChain(opnd3).
1047 AddOpndChain(opnd4).AddOpndChain(tmpReg1).AddOpndChain(tmpReg2);
1048 cgFunc->GetCurBB()->AppendInsn(insn);
1049 return dstReg;
1050 }
1051
SelectIsCOWArray(IntrinsicopNode & node,Operand & opnd0,Operand & opnd1,Operand & opnd2,Operand & opnd3,Operand & opnd4,Operand & opnd5,const BaseNode & parent)1052 RegOperand &X64MPIsel::SelectIsCOWArray(IntrinsicopNode &node, Operand &opnd0,
1053 Operand &opnd1, Operand &opnd2, Operand &opnd3, Operand &opnd4, Operand &opnd5, const BaseNode &parent)
1054 {
1055 RegOperand &dstReg = cgFunc->GetOpndBuilder()->CreateVReg(k8BitSize, kRegTyInt);
1056 RegOperand &tmpReg1 = cgFunc->GetOpndBuilder()->CreateVReg(k32BitSize, kRegTyInt);
1057 RegOperand &tmpReg2 = cgFunc->GetOpndBuilder()->CreateVReg(k64BitSize, kRegTyInt);
1058 MOperator mOp = x64::MOP_is_cow_array;
1059 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
1060 insn.AddOpndChain(dstReg).AddOpndChain(opnd0).AddOpndChain(opnd1).AddOpndChain(opnd2).AddOpndChain(opnd3).
1061 AddOpndChain(opnd4).AddOpndChain(opnd5).AddOpndChain(tmpReg1).AddOpndChain(tmpReg2);
1062 cgFunc->GetCurBB()->AppendInsn(insn);
1063 return dstReg;
1064 }
1065
GetTargetBasicPointer(PrimType primType)1066 RegOperand &X64MPIsel::GetTargetBasicPointer(PrimType primType)
1067 {
1068 return cgFunc->GetOpndBuilder()->CreatePReg(x64::RBP, GetPrimTypeBitSize(primType),
1069 cgFunc->GetRegTyFromPrimTy(primType));
1070 }
1071
SelectRetypeFloat(RegOperand & resOpnd,Operand & opnd0,PrimType toType,PrimType fromType)1072 void X64MPIsel::SelectRetypeFloat(RegOperand &resOpnd, Operand &opnd0, PrimType toType, PrimType fromType)
1073 {
1074 uint32 fromSize = GetPrimTypeBitSize(fromType);
1075 [[maybe_unused]] uint32 toSize = GetPrimTypeBitSize(toType);
1076 DEBUG_ASSERT(fromSize == toSize, "retype bit widith doesn' match");
1077 RegOperand ®Opnd0 = SelectCopy2Reg(opnd0, fromType);
1078 MOperator mOp = x64::MOP_begin;
1079 if (fromSize == k32BitSize) {
1080 mOp = IsPrimitiveFloat(fromType) ? x64::MOP_movd_fr_r : x64::MOP_movd_r_fr;
1081 } else if (fromSize == k64BitSize) {
1082 mOp = IsPrimitiveFloat(fromType) ? x64::MOP_movq_fr_r : x64::MOP_movq_r_fr;
1083 } else {
1084 CHECK_FATAL(false, "niy");
1085 }
1086 CHECK_FATAL(mOp != x64::MOP_begin, "NIY");
1087 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
1088 (void)insn.AddOpndChain(regOpnd0).AddOpndChain(resOpnd);
1089 cgFunc->GetCurBB()->AppendInsn(insn);
1090 return;
1091 }
1092
SelectSqrt(UnaryNode & node,Operand & src,const BaseNode & parent)1093 Operand *X64MPIsel::SelectSqrt(UnaryNode &node, Operand &src, const BaseNode &parent)
1094 {
1095 PrimType dtype = node.GetPrimType();
1096 if (!IsPrimitiveFloat(dtype)) {
1097 DEBUG_ASSERT(false, "should be float type");
1098 return nullptr;
1099 }
1100 auto bitSize = GetPrimTypeBitSize(dtype);
1101 MOperator mOp = x64::MOP_begin;
1102 if (bitSize == k64BitSize) {
1103 mOp = MOP_sqrtd_r_r;
1104 } else if (bitSize == k32BitSize) {
1105 mOp = MOP_sqrts_r_r;
1106 } else {
1107 CHECK_FATAL(false, "niy");
1108 }
1109 RegOperand ®Opnd0 = SelectCopy2Reg(src, dtype);
1110 Insn &insn = cgFunc->GetInsnBuilder()->BuildInsn(mOp, X64CG::kMd[mOp]);
1111 Operand &retReg = cgFunc->GetOpndBuilder()->CreateVReg(bitSize, cgFunc->GetRegTyFromPrimTy(dtype));
1112
1113 (void)insn.AddOpndChain(regOpnd0).AddOpndChain(retReg);
1114 cgFunc->GetCurBB()->AppendInsn(insn);
1115 return &retReg;
1116 }
1117 } // namespace maplebe
1118