15ffd83dbSDimitry Andric //===-- lib/CodeGen/GlobalISel/InlineAsmLowering.cpp ----------------------===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric /// 95ffd83dbSDimitry Andric /// \file 105ffd83dbSDimitry Andric /// This file implements the lowering from LLVM IR inline asm to MIR INLINEASM 115ffd83dbSDimitry Andric /// 125ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 135ffd83dbSDimitry Andric 145ffd83dbSDimitry Andric #include "llvm/CodeGen/GlobalISel/InlineAsmLowering.h" 155ffd83dbSDimitry Andric #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" 165ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineOperand.h" 175ffd83dbSDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 185ffd83dbSDimitry Andric #include "llvm/CodeGen/TargetLowering.h" 195ffd83dbSDimitry Andric #include "llvm/IR/Module.h" 205ffd83dbSDimitry Andric 215ffd83dbSDimitry Andric #define DEBUG_TYPE "inline-asm-lowering" 225ffd83dbSDimitry Andric 235ffd83dbSDimitry Andric using namespace llvm; 245ffd83dbSDimitry Andric 255ffd83dbSDimitry Andric void InlineAsmLowering::anchor() {} 265ffd83dbSDimitry Andric 275ffd83dbSDimitry Andric namespace { 285ffd83dbSDimitry Andric 295ffd83dbSDimitry Andric /// GISelAsmOperandInfo - This contains information for each constraint that we 305ffd83dbSDimitry Andric /// are lowering. 315ffd83dbSDimitry Andric class GISelAsmOperandInfo : public TargetLowering::AsmOperandInfo { 325ffd83dbSDimitry Andric public: 335ffd83dbSDimitry Andric /// Regs - If this is a register or register class operand, this 345ffd83dbSDimitry Andric /// contains the set of assigned registers corresponding to the operand. 355ffd83dbSDimitry Andric SmallVector<Register, 1> Regs; 365ffd83dbSDimitry Andric 375ffd83dbSDimitry Andric explicit GISelAsmOperandInfo(const TargetLowering::AsmOperandInfo &Info) 385ffd83dbSDimitry Andric : TargetLowering::AsmOperandInfo(Info) {} 395ffd83dbSDimitry Andric }; 405ffd83dbSDimitry Andric 415ffd83dbSDimitry Andric using GISelAsmOperandInfoVector = SmallVector<GISelAsmOperandInfo, 16>; 425ffd83dbSDimitry Andric 435ffd83dbSDimitry Andric class ExtraFlags { 445ffd83dbSDimitry Andric unsigned Flags = 0; 455ffd83dbSDimitry Andric 465ffd83dbSDimitry Andric public: 475ffd83dbSDimitry Andric explicit ExtraFlags(const CallBase &CB) { 485ffd83dbSDimitry Andric const InlineAsm *IA = cast<InlineAsm>(CB.getCalledOperand()); 495ffd83dbSDimitry Andric if (IA->hasSideEffects()) 505ffd83dbSDimitry Andric Flags |= InlineAsm::Extra_HasSideEffects; 515ffd83dbSDimitry Andric if (IA->isAlignStack()) 525ffd83dbSDimitry Andric Flags |= InlineAsm::Extra_IsAlignStack; 535ffd83dbSDimitry Andric if (CB.isConvergent()) 545ffd83dbSDimitry Andric Flags |= InlineAsm::Extra_IsConvergent; 555ffd83dbSDimitry Andric Flags |= IA->getDialect() * InlineAsm::Extra_AsmDialect; 565ffd83dbSDimitry Andric } 575ffd83dbSDimitry Andric 585ffd83dbSDimitry Andric void update(const TargetLowering::AsmOperandInfo &OpInfo) { 595ffd83dbSDimitry Andric // Ideally, we would only check against memory constraints. However, the 605ffd83dbSDimitry Andric // meaning of an Other constraint can be target-specific and we can't easily 615ffd83dbSDimitry Andric // reason about it. Therefore, be conservative and set MayLoad/MayStore 625ffd83dbSDimitry Andric // for Other constraints as well. 635ffd83dbSDimitry Andric if (OpInfo.ConstraintType == TargetLowering::C_Memory || 645ffd83dbSDimitry Andric OpInfo.ConstraintType == TargetLowering::C_Other) { 655ffd83dbSDimitry Andric if (OpInfo.Type == InlineAsm::isInput) 665ffd83dbSDimitry Andric Flags |= InlineAsm::Extra_MayLoad; 675ffd83dbSDimitry Andric else if (OpInfo.Type == InlineAsm::isOutput) 685ffd83dbSDimitry Andric Flags |= InlineAsm::Extra_MayStore; 695ffd83dbSDimitry Andric else if (OpInfo.Type == InlineAsm::isClobber) 705ffd83dbSDimitry Andric Flags |= (InlineAsm::Extra_MayLoad | InlineAsm::Extra_MayStore); 715ffd83dbSDimitry Andric } 725ffd83dbSDimitry Andric } 735ffd83dbSDimitry Andric 745ffd83dbSDimitry Andric unsigned get() const { return Flags; } 755ffd83dbSDimitry Andric }; 765ffd83dbSDimitry Andric 775ffd83dbSDimitry Andric } // namespace 785ffd83dbSDimitry Andric 795ffd83dbSDimitry Andric /// Assign virtual/physical registers for the specified register operand. 805ffd83dbSDimitry Andric static void getRegistersForValue(MachineFunction &MF, 815ffd83dbSDimitry Andric MachineIRBuilder &MIRBuilder, 825ffd83dbSDimitry Andric GISelAsmOperandInfo &OpInfo, 835ffd83dbSDimitry Andric GISelAsmOperandInfo &RefOpInfo) { 845ffd83dbSDimitry Andric 855ffd83dbSDimitry Andric const TargetLowering &TLI = *MF.getSubtarget().getTargetLowering(); 865ffd83dbSDimitry Andric const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); 875ffd83dbSDimitry Andric 885ffd83dbSDimitry Andric // No work to do for memory operations. 895ffd83dbSDimitry Andric if (OpInfo.ConstraintType == TargetLowering::C_Memory) 905ffd83dbSDimitry Andric return; 915ffd83dbSDimitry Andric 925ffd83dbSDimitry Andric // If this is a constraint for a single physreg, or a constraint for a 935ffd83dbSDimitry Andric // register class, find it. 945ffd83dbSDimitry Andric Register AssignedReg; 955ffd83dbSDimitry Andric const TargetRegisterClass *RC; 965ffd83dbSDimitry Andric std::tie(AssignedReg, RC) = TLI.getRegForInlineAsmConstraint( 975ffd83dbSDimitry Andric &TRI, RefOpInfo.ConstraintCode, RefOpInfo.ConstraintVT); 985ffd83dbSDimitry Andric // RC is unset only on failure. Return immediately. 995ffd83dbSDimitry Andric if (!RC) 1005ffd83dbSDimitry Andric return; 1015ffd83dbSDimitry Andric 1025ffd83dbSDimitry Andric // No need to allocate a matching input constraint since the constraint it's 1035ffd83dbSDimitry Andric // matching to has already been allocated. 1045ffd83dbSDimitry Andric if (OpInfo.isMatchingInputConstraint()) 1055ffd83dbSDimitry Andric return; 1065ffd83dbSDimitry Andric 1075ffd83dbSDimitry Andric // Initialize NumRegs. 1085ffd83dbSDimitry Andric unsigned NumRegs = 1; 1095ffd83dbSDimitry Andric if (OpInfo.ConstraintVT != MVT::Other) 1105ffd83dbSDimitry Andric NumRegs = 1115ffd83dbSDimitry Andric TLI.getNumRegisters(MF.getFunction().getContext(), OpInfo.ConstraintVT); 1125ffd83dbSDimitry Andric 1135ffd83dbSDimitry Andric // If this is a constraint for a specific physical register, but the type of 1145ffd83dbSDimitry Andric // the operand requires more than one register to be passed, we allocate the 1155ffd83dbSDimitry Andric // required amount of physical registers, starting from the selected physical 1165ffd83dbSDimitry Andric // register. 1175ffd83dbSDimitry Andric // For this, first retrieve a register iterator for the given register class 1185ffd83dbSDimitry Andric TargetRegisterClass::iterator I = RC->begin(); 1195ffd83dbSDimitry Andric MachineRegisterInfo &RegInfo = MF.getRegInfo(); 1205ffd83dbSDimitry Andric 1215ffd83dbSDimitry Andric // Advance the iterator to the assigned register (if set) 1225ffd83dbSDimitry Andric if (AssignedReg) { 1235ffd83dbSDimitry Andric for (; *I != AssignedReg; ++I) 1245ffd83dbSDimitry Andric assert(I != RC->end() && "AssignedReg should be a member of provided RC"); 1255ffd83dbSDimitry Andric } 1265ffd83dbSDimitry Andric 1275ffd83dbSDimitry Andric // Finally, assign the registers. If the AssignedReg isn't set, create virtual 1285ffd83dbSDimitry Andric // registers with the provided register class 1295ffd83dbSDimitry Andric for (; NumRegs; --NumRegs, ++I) { 1305ffd83dbSDimitry Andric assert(I != RC->end() && "Ran out of registers to allocate!"); 1315ffd83dbSDimitry Andric Register R = AssignedReg ? Register(*I) : RegInfo.createVirtualRegister(RC); 1325ffd83dbSDimitry Andric OpInfo.Regs.push_back(R); 1335ffd83dbSDimitry Andric } 1345ffd83dbSDimitry Andric } 1355ffd83dbSDimitry Andric 1365ffd83dbSDimitry Andric static void computeConstraintToUse(const TargetLowering *TLI, 1375ffd83dbSDimitry Andric TargetLowering::AsmOperandInfo &OpInfo) { 1385ffd83dbSDimitry Andric assert(!OpInfo.Codes.empty() && "Must have at least one constraint"); 1395ffd83dbSDimitry Andric 1405ffd83dbSDimitry Andric // Single-letter constraints ('r') are very common. 1415ffd83dbSDimitry Andric if (OpInfo.Codes.size() == 1) { 1425ffd83dbSDimitry Andric OpInfo.ConstraintCode = OpInfo.Codes[0]; 1435ffd83dbSDimitry Andric OpInfo.ConstraintType = TLI->getConstraintType(OpInfo.ConstraintCode); 1445ffd83dbSDimitry Andric } else { 1455f757f3fSDimitry Andric TargetLowering::ConstraintGroup G = TLI->getConstraintPreferences(OpInfo); 1465f757f3fSDimitry Andric if (G.empty()) 1475f757f3fSDimitry Andric return; 1485f757f3fSDimitry Andric // FIXME: prefer immediate constraints if the target allows it 1495f757f3fSDimitry Andric unsigned BestIdx = 0; 1505f757f3fSDimitry Andric for (const unsigned E = G.size(); 1515f757f3fSDimitry Andric BestIdx < E && (G[BestIdx].second == TargetLowering::C_Other || 1525f757f3fSDimitry Andric G[BestIdx].second == TargetLowering::C_Immediate); 1535f757f3fSDimitry Andric ++BestIdx) 1545f757f3fSDimitry Andric ; 1555f757f3fSDimitry Andric OpInfo.ConstraintCode = G[BestIdx].first; 1565f757f3fSDimitry Andric OpInfo.ConstraintType = G[BestIdx].second; 1575ffd83dbSDimitry Andric } 1585ffd83dbSDimitry Andric 1595ffd83dbSDimitry Andric // 'X' matches anything. 1605ffd83dbSDimitry Andric if (OpInfo.ConstraintCode == "X" && OpInfo.CallOperandVal) { 1615ffd83dbSDimitry Andric // Labels and constants are handled elsewhere ('X' is the only thing 1625ffd83dbSDimitry Andric // that matches labels). For Functions, the type here is the type of 1635ffd83dbSDimitry Andric // the result, which is not what we want to look at; leave them alone. 1645ffd83dbSDimitry Andric Value *Val = OpInfo.CallOperandVal; 1655ffd83dbSDimitry Andric if (isa<BasicBlock>(Val) || isa<ConstantInt>(Val) || isa<Function>(Val)) 1665ffd83dbSDimitry Andric return; 1675ffd83dbSDimitry Andric 1685ffd83dbSDimitry Andric // Otherwise, try to resolve it to something we know about by looking at 1695ffd83dbSDimitry Andric // the actual operand type. 1705ffd83dbSDimitry Andric if (const char *Repl = TLI->LowerXConstraint(OpInfo.ConstraintVT)) { 1715ffd83dbSDimitry Andric OpInfo.ConstraintCode = Repl; 1725ffd83dbSDimitry Andric OpInfo.ConstraintType = TLI->getConstraintType(OpInfo.ConstraintCode); 1735ffd83dbSDimitry Andric } 1745ffd83dbSDimitry Andric } 1755ffd83dbSDimitry Andric } 1765ffd83dbSDimitry Andric 1775ffd83dbSDimitry Andric static unsigned getNumOpRegs(const MachineInstr &I, unsigned OpIdx) { 1785f757f3fSDimitry Andric const InlineAsm::Flag F(I.getOperand(OpIdx).getImm()); 1795f757f3fSDimitry Andric return F.getNumOperandRegisters(); 1805ffd83dbSDimitry Andric } 1815ffd83dbSDimitry Andric 1825ffd83dbSDimitry Andric static bool buildAnyextOrCopy(Register Dst, Register Src, 1835ffd83dbSDimitry Andric MachineIRBuilder &MIRBuilder) { 1845ffd83dbSDimitry Andric const TargetRegisterInfo *TRI = 1855ffd83dbSDimitry Andric MIRBuilder.getMF().getSubtarget().getRegisterInfo(); 1865ffd83dbSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 1875ffd83dbSDimitry Andric 1885ffd83dbSDimitry Andric auto SrcTy = MRI->getType(Src); 1895ffd83dbSDimitry Andric if (!SrcTy.isValid()) { 1905ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Source type for copy is not valid\n"); 1915ffd83dbSDimitry Andric return false; 1925ffd83dbSDimitry Andric } 1935ffd83dbSDimitry Andric unsigned SrcSize = TRI->getRegSizeInBits(Src, *MRI); 1945ffd83dbSDimitry Andric unsigned DstSize = TRI->getRegSizeInBits(Dst, *MRI); 1955ffd83dbSDimitry Andric 1965ffd83dbSDimitry Andric if (DstSize < SrcSize) { 1975ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Input can't fit in destination reg class\n"); 1985ffd83dbSDimitry Andric return false; 1995ffd83dbSDimitry Andric } 2005ffd83dbSDimitry Andric 2015ffd83dbSDimitry Andric // Attempt to anyext small scalar sources. 2025ffd83dbSDimitry Andric if (DstSize > SrcSize) { 2035ffd83dbSDimitry Andric if (!SrcTy.isScalar()) { 2045ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Can't extend non-scalar input to size of" 2055ffd83dbSDimitry Andric "destination register class\n"); 2065ffd83dbSDimitry Andric return false; 2075ffd83dbSDimitry Andric } 2085ffd83dbSDimitry Andric Src = MIRBuilder.buildAnyExt(LLT::scalar(DstSize), Src).getReg(0); 2095ffd83dbSDimitry Andric } 2105ffd83dbSDimitry Andric 2115ffd83dbSDimitry Andric MIRBuilder.buildCopy(Dst, Src); 2125ffd83dbSDimitry Andric return true; 2135ffd83dbSDimitry Andric } 2145ffd83dbSDimitry Andric 2155ffd83dbSDimitry Andric bool InlineAsmLowering::lowerInlineAsm( 2165ffd83dbSDimitry Andric MachineIRBuilder &MIRBuilder, const CallBase &Call, 2175ffd83dbSDimitry Andric std::function<ArrayRef<Register>(const Value &Val)> GetOrCreateVRegs) 2185ffd83dbSDimitry Andric const { 2195ffd83dbSDimitry Andric const InlineAsm *IA = cast<InlineAsm>(Call.getCalledOperand()); 2205ffd83dbSDimitry Andric 2215ffd83dbSDimitry Andric /// ConstraintOperands - Information about all of the constraints. 2225ffd83dbSDimitry Andric GISelAsmOperandInfoVector ConstraintOperands; 2235ffd83dbSDimitry Andric 2245ffd83dbSDimitry Andric MachineFunction &MF = MIRBuilder.getMF(); 2255ffd83dbSDimitry Andric const Function &F = MF.getFunction(); 226*0fca6ea1SDimitry Andric const DataLayout &DL = F.getDataLayout(); 2275ffd83dbSDimitry Andric const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); 2285ffd83dbSDimitry Andric 2295ffd83dbSDimitry Andric MachineRegisterInfo *MRI = MIRBuilder.getMRI(); 2305ffd83dbSDimitry Andric 2315ffd83dbSDimitry Andric TargetLowering::AsmOperandInfoVector TargetConstraints = 2325ffd83dbSDimitry Andric TLI->ParseConstraints(DL, TRI, Call); 2335ffd83dbSDimitry Andric 2345ffd83dbSDimitry Andric ExtraFlags ExtraInfo(Call); 2355ffd83dbSDimitry Andric unsigned ArgNo = 0; // ArgNo - The argument of the CallInst. 2365ffd83dbSDimitry Andric unsigned ResNo = 0; // ResNo - The result number of the next output. 2375ffd83dbSDimitry Andric for (auto &T : TargetConstraints) { 2385ffd83dbSDimitry Andric ConstraintOperands.push_back(GISelAsmOperandInfo(T)); 2395ffd83dbSDimitry Andric GISelAsmOperandInfo &OpInfo = ConstraintOperands.back(); 2405ffd83dbSDimitry Andric 2415ffd83dbSDimitry Andric // Compute the value type for each operand. 24204eeddc0SDimitry Andric if (OpInfo.hasArg()) { 24304eeddc0SDimitry Andric OpInfo.CallOperandVal = const_cast<Value *>(Call.getArgOperand(ArgNo)); 2445ffd83dbSDimitry Andric 2455ffd83dbSDimitry Andric if (isa<BasicBlock>(OpInfo.CallOperandVal)) { 2465ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Basic block input operands not supported yet\n"); 2475ffd83dbSDimitry Andric return false; 2485ffd83dbSDimitry Andric } 2495ffd83dbSDimitry Andric 2505ffd83dbSDimitry Andric Type *OpTy = OpInfo.CallOperandVal->getType(); 2515ffd83dbSDimitry Andric 2525ffd83dbSDimitry Andric // If this is an indirect operand, the operand is a pointer to the 2535ffd83dbSDimitry Andric // accessed type. 2545ffd83dbSDimitry Andric if (OpInfo.isIndirect) { 25581ad6265SDimitry Andric OpTy = Call.getParamElementType(ArgNo); 25604eeddc0SDimitry Andric assert(OpTy && "Indirect operand must have elementtype attribute"); 2575ffd83dbSDimitry Andric } 2585ffd83dbSDimitry Andric 2595ffd83dbSDimitry Andric // FIXME: Support aggregate input operands 2605ffd83dbSDimitry Andric if (!OpTy->isSingleValueType()) { 2615ffd83dbSDimitry Andric LLVM_DEBUG( 2625ffd83dbSDimitry Andric dbgs() << "Aggregate input operands are not supported yet\n"); 2635ffd83dbSDimitry Andric return false; 2645ffd83dbSDimitry Andric } 2655ffd83dbSDimitry Andric 2666e75b2fbSDimitry Andric OpInfo.ConstraintVT = 2676e75b2fbSDimitry Andric TLI->getAsmOperandValueType(DL, OpTy, true).getSimpleVT(); 26804eeddc0SDimitry Andric ++ArgNo; 2695ffd83dbSDimitry Andric } else if (OpInfo.Type == InlineAsm::isOutput && !OpInfo.isIndirect) { 2705ffd83dbSDimitry Andric assert(!Call.getType()->isVoidTy() && "Bad inline asm!"); 2715ffd83dbSDimitry Andric if (StructType *STy = dyn_cast<StructType>(Call.getType())) { 2725ffd83dbSDimitry Andric OpInfo.ConstraintVT = 2735ffd83dbSDimitry Andric TLI->getSimpleValueType(DL, STy->getElementType(ResNo)); 2745ffd83dbSDimitry Andric } else { 2755ffd83dbSDimitry Andric assert(ResNo == 0 && "Asm only has one result!"); 2766e75b2fbSDimitry Andric OpInfo.ConstraintVT = 2776e75b2fbSDimitry Andric TLI->getAsmOperandValueType(DL, Call.getType()).getSimpleVT(); 2785ffd83dbSDimitry Andric } 2795ffd83dbSDimitry Andric ++ResNo; 2805ffd83dbSDimitry Andric } else { 281fcaf7f86SDimitry Andric assert(OpInfo.Type != InlineAsm::isLabel && 282fcaf7f86SDimitry Andric "GlobalISel currently doesn't support callbr"); 2835ffd83dbSDimitry Andric OpInfo.ConstraintVT = MVT::Other; 2845ffd83dbSDimitry Andric } 2855ffd83dbSDimitry Andric 2866e75b2fbSDimitry Andric if (OpInfo.ConstraintVT == MVT::i64x8) 2876e75b2fbSDimitry Andric return false; 2886e75b2fbSDimitry Andric 2895ffd83dbSDimitry Andric // Compute the constraint code and ConstraintType to use. 2905ffd83dbSDimitry Andric computeConstraintToUse(TLI, OpInfo); 2915ffd83dbSDimitry Andric 2925ffd83dbSDimitry Andric // The selected constraint type might expose new sideeffects 2935ffd83dbSDimitry Andric ExtraInfo.update(OpInfo); 2945ffd83dbSDimitry Andric } 2955ffd83dbSDimitry Andric 2965ffd83dbSDimitry Andric // At this point, all operand types are decided. 2975ffd83dbSDimitry Andric // Create the MachineInstr, but don't insert it yet since input 2985ffd83dbSDimitry Andric // operands still need to insert instructions before this one 2995ffd83dbSDimitry Andric auto Inst = MIRBuilder.buildInstrNoInsert(TargetOpcode::INLINEASM) 3005ffd83dbSDimitry Andric .addExternalSymbol(IA->getAsmString().c_str()) 3015ffd83dbSDimitry Andric .addImm(ExtraInfo.get()); 3025ffd83dbSDimitry Andric 3035ffd83dbSDimitry Andric // Starting from this operand: flag followed by register(s) will be added as 3045ffd83dbSDimitry Andric // operands to Inst for each constraint. Used for matching input constraints. 3055ffd83dbSDimitry Andric unsigned StartIdx = Inst->getNumOperands(); 3065ffd83dbSDimitry Andric 3075ffd83dbSDimitry Andric // Collects the output operands for later processing 3085ffd83dbSDimitry Andric GISelAsmOperandInfoVector OutputOperands; 3095ffd83dbSDimitry Andric 3105ffd83dbSDimitry Andric for (auto &OpInfo : ConstraintOperands) { 3115ffd83dbSDimitry Andric GISelAsmOperandInfo &RefOpInfo = 3125ffd83dbSDimitry Andric OpInfo.isMatchingInputConstraint() 3135ffd83dbSDimitry Andric ? ConstraintOperands[OpInfo.getMatchedOperand()] 3145ffd83dbSDimitry Andric : OpInfo; 3155ffd83dbSDimitry Andric 3165ffd83dbSDimitry Andric // Assign registers for register operands 3175ffd83dbSDimitry Andric getRegistersForValue(MF, MIRBuilder, OpInfo, RefOpInfo); 3185ffd83dbSDimitry Andric 3195ffd83dbSDimitry Andric switch (OpInfo.Type) { 3205ffd83dbSDimitry Andric case InlineAsm::isOutput: 3215ffd83dbSDimitry Andric if (OpInfo.ConstraintType == TargetLowering::C_Memory) { 3225f757f3fSDimitry Andric const InlineAsm::ConstraintCode ConstraintID = 3235ffd83dbSDimitry Andric TLI->getInlineAsmMemConstraint(OpInfo.ConstraintCode); 3245f757f3fSDimitry Andric assert(ConstraintID != InlineAsm::ConstraintCode::Unknown && 3255ffd83dbSDimitry Andric "Failed to convert memory constraint code to constraint id."); 3265ffd83dbSDimitry Andric 3275ffd83dbSDimitry Andric // Add information to the INLINEASM instruction to know about this 3285ffd83dbSDimitry Andric // output. 3295f757f3fSDimitry Andric InlineAsm::Flag Flag(InlineAsm::Kind::Mem, 1); 3305f757f3fSDimitry Andric Flag.setMemConstraint(ConstraintID); 3315f757f3fSDimitry Andric Inst.addImm(Flag); 3325ffd83dbSDimitry Andric ArrayRef<Register> SourceRegs = 3335ffd83dbSDimitry Andric GetOrCreateVRegs(*OpInfo.CallOperandVal); 3345ffd83dbSDimitry Andric assert( 3355ffd83dbSDimitry Andric SourceRegs.size() == 1 && 3365ffd83dbSDimitry Andric "Expected the memory output to fit into a single virtual register"); 3375ffd83dbSDimitry Andric Inst.addReg(SourceRegs[0]); 3385ffd83dbSDimitry Andric } else { 3395ffd83dbSDimitry Andric // Otherwise, this outputs to a register (directly for C_Register / 34006c3fb27SDimitry Andric // C_RegisterClass/C_Other. 3415ffd83dbSDimitry Andric assert(OpInfo.ConstraintType == TargetLowering::C_Register || 34206c3fb27SDimitry Andric OpInfo.ConstraintType == TargetLowering::C_RegisterClass || 34306c3fb27SDimitry Andric OpInfo.ConstraintType == TargetLowering::C_Other); 3445ffd83dbSDimitry Andric 34506c3fb27SDimitry Andric // Find a register that we can use. 3465ffd83dbSDimitry Andric if (OpInfo.Regs.empty()) { 3475ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() 3485ffd83dbSDimitry Andric << "Couldn't allocate output register for constraint\n"); 3495ffd83dbSDimitry Andric return false; 3505ffd83dbSDimitry Andric } 3515ffd83dbSDimitry Andric 3525ffd83dbSDimitry Andric // Add information to the INLINEASM instruction to know that this 3535ffd83dbSDimitry Andric // register is set. 3545f757f3fSDimitry Andric InlineAsm::Flag Flag(OpInfo.isEarlyClobber 3555f757f3fSDimitry Andric ? InlineAsm::Kind::RegDefEarlyClobber 3565f757f3fSDimitry Andric : InlineAsm::Kind::RegDef, 3575ffd83dbSDimitry Andric OpInfo.Regs.size()); 3585ffd83dbSDimitry Andric if (OpInfo.Regs.front().isVirtual()) { 3595ffd83dbSDimitry Andric // Put the register class of the virtual registers in the flag word. 3605ffd83dbSDimitry Andric // That way, later passes can recompute register class constraints for 3615ffd83dbSDimitry Andric // inline assembly as well as normal instructions. Don't do this for 3625ffd83dbSDimitry Andric // tied operands that can use the regclass information from the def. 3635ffd83dbSDimitry Andric const TargetRegisterClass *RC = MRI->getRegClass(OpInfo.Regs.front()); 3645f757f3fSDimitry Andric Flag.setRegClass(RC->getID()); 3655ffd83dbSDimitry Andric } 3665ffd83dbSDimitry Andric 3675ffd83dbSDimitry Andric Inst.addImm(Flag); 3685ffd83dbSDimitry Andric 3695ffd83dbSDimitry Andric for (Register Reg : OpInfo.Regs) { 3705ffd83dbSDimitry Andric Inst.addReg(Reg, 3715ffd83dbSDimitry Andric RegState::Define | getImplRegState(Reg.isPhysical()) | 3725ffd83dbSDimitry Andric (OpInfo.isEarlyClobber ? RegState::EarlyClobber : 0)); 3735ffd83dbSDimitry Andric } 3745ffd83dbSDimitry Andric 3755ffd83dbSDimitry Andric // Remember this output operand for later processing 3765ffd83dbSDimitry Andric OutputOperands.push_back(OpInfo); 3775ffd83dbSDimitry Andric } 3785ffd83dbSDimitry Andric 3795ffd83dbSDimitry Andric break; 380fcaf7f86SDimitry Andric case InlineAsm::isInput: 381fcaf7f86SDimitry Andric case InlineAsm::isLabel: { 3825ffd83dbSDimitry Andric if (OpInfo.isMatchingInputConstraint()) { 3835ffd83dbSDimitry Andric unsigned DefIdx = OpInfo.getMatchedOperand(); 3845ffd83dbSDimitry Andric // Find operand with register def that corresponds to DefIdx. 3855ffd83dbSDimitry Andric unsigned InstFlagIdx = StartIdx; 3865ffd83dbSDimitry Andric for (unsigned i = 0; i < DefIdx; ++i) 3875ffd83dbSDimitry Andric InstFlagIdx += getNumOpRegs(*Inst, InstFlagIdx) + 1; 3885ffd83dbSDimitry Andric assert(getNumOpRegs(*Inst, InstFlagIdx) == 1 && "Wrong flag"); 3895ffd83dbSDimitry Andric 3905f757f3fSDimitry Andric const InlineAsm::Flag MatchedOperandFlag(Inst->getOperand(InstFlagIdx).getImm()); 3915f757f3fSDimitry Andric if (MatchedOperandFlag.isMemKind()) { 3925ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Matching input constraint to mem operand not " 3935ffd83dbSDimitry Andric "supported. This should be target specific.\n"); 3945ffd83dbSDimitry Andric return false; 3955ffd83dbSDimitry Andric } 3965f757f3fSDimitry Andric if (!MatchedOperandFlag.isRegDefKind() && !MatchedOperandFlag.isRegDefEarlyClobberKind()) { 3975ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Unknown matching constraint\n"); 3985ffd83dbSDimitry Andric return false; 3995ffd83dbSDimitry Andric } 4005ffd83dbSDimitry Andric 4015ffd83dbSDimitry Andric // We want to tie input to register in next operand. 4025ffd83dbSDimitry Andric unsigned DefRegIdx = InstFlagIdx + 1; 4035ffd83dbSDimitry Andric Register Def = Inst->getOperand(DefRegIdx).getReg(); 4045ffd83dbSDimitry Andric 4055ffd83dbSDimitry Andric ArrayRef<Register> SrcRegs = GetOrCreateVRegs(*OpInfo.CallOperandVal); 4065ffd83dbSDimitry Andric assert(SrcRegs.size() == 1 && "Single register is expected here"); 4075ffd83dbSDimitry Andric 4081106035dSDimitry Andric // When Def is physreg: use given input. 4091106035dSDimitry Andric Register In = SrcRegs[0]; 4101106035dSDimitry Andric // When Def is vreg: copy input to new vreg with same reg class as Def. 4111106035dSDimitry Andric if (Def.isVirtual()) { 4121106035dSDimitry Andric In = MRI->createVirtualRegister(MRI->getRegClass(Def)); 4131106035dSDimitry Andric if (!buildAnyextOrCopy(In, SrcRegs[0], MIRBuilder)) 4141106035dSDimitry Andric return false; 4151106035dSDimitry Andric } 4161106035dSDimitry Andric 4171106035dSDimitry Andric // Add Flag and input register operand (In) to Inst. Tie In to Def. 4185f757f3fSDimitry Andric InlineAsm::Flag UseFlag(InlineAsm::Kind::RegUse, 1); 4195f757f3fSDimitry Andric UseFlag.setMatchingOp(DefIdx); 4205f757f3fSDimitry Andric Inst.addImm(UseFlag); 4211106035dSDimitry Andric Inst.addReg(In); 4225ffd83dbSDimitry Andric Inst->tieOperands(DefRegIdx, Inst->getNumOperands() - 1); 4235ffd83dbSDimitry Andric break; 4245ffd83dbSDimitry Andric } 4255ffd83dbSDimitry Andric 4265ffd83dbSDimitry Andric if (OpInfo.ConstraintType == TargetLowering::C_Other && 4275ffd83dbSDimitry Andric OpInfo.isIndirect) { 4285ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Indirect input operands with unknown constraint " 4295ffd83dbSDimitry Andric "not supported yet\n"); 4305ffd83dbSDimitry Andric return false; 4315ffd83dbSDimitry Andric } 4325ffd83dbSDimitry Andric 4335ffd83dbSDimitry Andric if (OpInfo.ConstraintType == TargetLowering::C_Immediate || 4345ffd83dbSDimitry Andric OpInfo.ConstraintType == TargetLowering::C_Other) { 4355ffd83dbSDimitry Andric 4365ffd83dbSDimitry Andric std::vector<MachineOperand> Ops; 4375ffd83dbSDimitry Andric if (!lowerAsmOperandForConstraint(OpInfo.CallOperandVal, 4385ffd83dbSDimitry Andric OpInfo.ConstraintCode, Ops, 4395ffd83dbSDimitry Andric MIRBuilder)) { 4405ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Don't support constraint: " 4415ffd83dbSDimitry Andric << OpInfo.ConstraintCode << " yet\n"); 4425ffd83dbSDimitry Andric return false; 4435ffd83dbSDimitry Andric } 4445ffd83dbSDimitry Andric 4455ffd83dbSDimitry Andric assert(Ops.size() > 0 && 4465ffd83dbSDimitry Andric "Expected constraint to be lowered to at least one operand"); 4475ffd83dbSDimitry Andric 4485ffd83dbSDimitry Andric // Add information to the INLINEASM node to know about this input. 4495f757f3fSDimitry Andric const unsigned OpFlags = 4505f757f3fSDimitry Andric InlineAsm::Flag(InlineAsm::Kind::Imm, Ops.size()); 4515ffd83dbSDimitry Andric Inst.addImm(OpFlags); 4525ffd83dbSDimitry Andric Inst.add(Ops); 4535ffd83dbSDimitry Andric break; 4545ffd83dbSDimitry Andric } 4555ffd83dbSDimitry Andric 4565ffd83dbSDimitry Andric if (OpInfo.ConstraintType == TargetLowering::C_Memory) { 4575ffd83dbSDimitry Andric 4585ffd83dbSDimitry Andric if (!OpInfo.isIndirect) { 4595ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() 4605ffd83dbSDimitry Andric << "Cannot indirectify memory input operands yet\n"); 4615ffd83dbSDimitry Andric return false; 4625ffd83dbSDimitry Andric } 4635ffd83dbSDimitry Andric 4645ffd83dbSDimitry Andric assert(OpInfo.isIndirect && "Operand must be indirect to be a mem!"); 4655ffd83dbSDimitry Andric 4665f757f3fSDimitry Andric const InlineAsm::ConstraintCode ConstraintID = 4675ffd83dbSDimitry Andric TLI->getInlineAsmMemConstraint(OpInfo.ConstraintCode); 4685f757f3fSDimitry Andric InlineAsm::Flag OpFlags(InlineAsm::Kind::Mem, 1); 4695f757f3fSDimitry Andric OpFlags.setMemConstraint(ConstraintID); 4705ffd83dbSDimitry Andric Inst.addImm(OpFlags); 4715ffd83dbSDimitry Andric ArrayRef<Register> SourceRegs = 4725ffd83dbSDimitry Andric GetOrCreateVRegs(*OpInfo.CallOperandVal); 4735ffd83dbSDimitry Andric assert( 4745ffd83dbSDimitry Andric SourceRegs.size() == 1 && 4755ffd83dbSDimitry Andric "Expected the memory input to fit into a single virtual register"); 4765ffd83dbSDimitry Andric Inst.addReg(SourceRegs[0]); 4775ffd83dbSDimitry Andric break; 4785ffd83dbSDimitry Andric } 4795ffd83dbSDimitry Andric 4805ffd83dbSDimitry Andric assert((OpInfo.ConstraintType == TargetLowering::C_RegisterClass || 4815ffd83dbSDimitry Andric OpInfo.ConstraintType == TargetLowering::C_Register) && 4825ffd83dbSDimitry Andric "Unknown constraint type!"); 4835ffd83dbSDimitry Andric 4845ffd83dbSDimitry Andric if (OpInfo.isIndirect) { 4855ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Can't handle indirect register inputs yet " 4865ffd83dbSDimitry Andric "for constraint '" 4875ffd83dbSDimitry Andric << OpInfo.ConstraintCode << "'\n"); 4885ffd83dbSDimitry Andric return false; 4895ffd83dbSDimitry Andric } 4905ffd83dbSDimitry Andric 4915ffd83dbSDimitry Andric // Copy the input into the appropriate registers. 4925ffd83dbSDimitry Andric if (OpInfo.Regs.empty()) { 4935ffd83dbSDimitry Andric LLVM_DEBUG( 4945ffd83dbSDimitry Andric dbgs() 4955ffd83dbSDimitry Andric << "Couldn't allocate input register for register constraint\n"); 4965ffd83dbSDimitry Andric return false; 4975ffd83dbSDimitry Andric } 4985ffd83dbSDimitry Andric 4995ffd83dbSDimitry Andric unsigned NumRegs = OpInfo.Regs.size(); 5005ffd83dbSDimitry Andric ArrayRef<Register> SourceRegs = GetOrCreateVRegs(*OpInfo.CallOperandVal); 5015ffd83dbSDimitry Andric assert(NumRegs == SourceRegs.size() && 5025ffd83dbSDimitry Andric "Expected the number of input registers to match the number of " 5035ffd83dbSDimitry Andric "source registers"); 5045ffd83dbSDimitry Andric 5055ffd83dbSDimitry Andric if (NumRegs > 1) { 5065ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Input operands with multiple input registers are " 5075ffd83dbSDimitry Andric "not supported yet\n"); 5085ffd83dbSDimitry Andric return false; 5095ffd83dbSDimitry Andric } 5105ffd83dbSDimitry Andric 5115f757f3fSDimitry Andric InlineAsm::Flag Flag(InlineAsm::Kind::RegUse, NumRegs); 512e8d8bef9SDimitry Andric if (OpInfo.Regs.front().isVirtual()) { 513e8d8bef9SDimitry Andric // Put the register class of the virtual registers in the flag word. 514e8d8bef9SDimitry Andric const TargetRegisterClass *RC = MRI->getRegClass(OpInfo.Regs.front()); 5155f757f3fSDimitry Andric Flag.setRegClass(RC->getID()); 516e8d8bef9SDimitry Andric } 5175ffd83dbSDimitry Andric Inst.addImm(Flag); 5185ffd83dbSDimitry Andric if (!buildAnyextOrCopy(OpInfo.Regs[0], SourceRegs[0], MIRBuilder)) 5195ffd83dbSDimitry Andric return false; 5205ffd83dbSDimitry Andric Inst.addReg(OpInfo.Regs[0]); 5215ffd83dbSDimitry Andric break; 5225ffd83dbSDimitry Andric } 5235ffd83dbSDimitry Andric 5245ffd83dbSDimitry Andric case InlineAsm::isClobber: { 5255ffd83dbSDimitry Andric 5265f757f3fSDimitry Andric const unsigned NumRegs = OpInfo.Regs.size(); 5275ffd83dbSDimitry Andric if (NumRegs > 0) { 5285f757f3fSDimitry Andric unsigned Flag = InlineAsm::Flag(InlineAsm::Kind::Clobber, NumRegs); 5295ffd83dbSDimitry Andric Inst.addImm(Flag); 5305ffd83dbSDimitry Andric 5315ffd83dbSDimitry Andric for (Register Reg : OpInfo.Regs) { 5325ffd83dbSDimitry Andric Inst.addReg(Reg, RegState::Define | RegState::EarlyClobber | 5335ffd83dbSDimitry Andric getImplRegState(Reg.isPhysical())); 5345ffd83dbSDimitry Andric } 5355ffd83dbSDimitry Andric } 5365ffd83dbSDimitry Andric break; 5375ffd83dbSDimitry Andric } 5385ffd83dbSDimitry Andric } 5395ffd83dbSDimitry Andric } 5405ffd83dbSDimitry Andric 541*0fca6ea1SDimitry Andric // Add rounding control registers as implicit def for inline asm. 542*0fca6ea1SDimitry Andric if (MF.getFunction().hasFnAttribute(Attribute::StrictFP)) { 543*0fca6ea1SDimitry Andric ArrayRef<MCPhysReg> RCRegs = TLI->getRoundingControlRegisters(); 544*0fca6ea1SDimitry Andric for (MCPhysReg Reg : RCRegs) 545*0fca6ea1SDimitry Andric Inst.addReg(Reg, RegState::ImplicitDefine); 546*0fca6ea1SDimitry Andric } 547*0fca6ea1SDimitry Andric 548*0fca6ea1SDimitry Andric if (auto Bundle = Call.getOperandBundle(LLVMContext::OB_convergencectrl)) { 549*0fca6ea1SDimitry Andric auto *Token = Bundle->Inputs[0].get(); 550*0fca6ea1SDimitry Andric ArrayRef<Register> SourceRegs = GetOrCreateVRegs(*Token); 551*0fca6ea1SDimitry Andric assert(SourceRegs.size() == 1 && 552*0fca6ea1SDimitry Andric "Expected the control token to fit into a single virtual register"); 553*0fca6ea1SDimitry Andric Inst.addUse(SourceRegs[0], RegState::Implicit); 554*0fca6ea1SDimitry Andric } 555*0fca6ea1SDimitry Andric 5565ffd83dbSDimitry Andric if (const MDNode *SrcLoc = Call.getMetadata("srcloc")) 5575ffd83dbSDimitry Andric Inst.addMetadata(SrcLoc); 5585ffd83dbSDimitry Andric 5595ffd83dbSDimitry Andric // All inputs are handled, insert the instruction now 5605ffd83dbSDimitry Andric MIRBuilder.insertInstr(Inst); 5615ffd83dbSDimitry Andric 5625ffd83dbSDimitry Andric // Finally, copy the output operands into the output registers 5635ffd83dbSDimitry Andric ArrayRef<Register> ResRegs = GetOrCreateVRegs(Call); 5645ffd83dbSDimitry Andric if (ResRegs.size() != OutputOperands.size()) { 5655ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Expected the number of output registers to match the " 5665ffd83dbSDimitry Andric "number of destination registers\n"); 5675ffd83dbSDimitry Andric return false; 5685ffd83dbSDimitry Andric } 5695ffd83dbSDimitry Andric for (unsigned int i = 0, e = ResRegs.size(); i < e; i++) { 5705ffd83dbSDimitry Andric GISelAsmOperandInfo &OpInfo = OutputOperands[i]; 5715ffd83dbSDimitry Andric 5725ffd83dbSDimitry Andric if (OpInfo.Regs.empty()) 5735ffd83dbSDimitry Andric continue; 5745ffd83dbSDimitry Andric 5755ffd83dbSDimitry Andric switch (OpInfo.ConstraintType) { 5765ffd83dbSDimitry Andric case TargetLowering::C_Register: 5775ffd83dbSDimitry Andric case TargetLowering::C_RegisterClass: { 5785ffd83dbSDimitry Andric if (OpInfo.Regs.size() > 1) { 5795ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Output operands with multiple defining " 5805ffd83dbSDimitry Andric "registers are not supported yet\n"); 5815ffd83dbSDimitry Andric return false; 5825ffd83dbSDimitry Andric } 5835ffd83dbSDimitry Andric 5845ffd83dbSDimitry Andric Register SrcReg = OpInfo.Regs[0]; 5855ffd83dbSDimitry Andric unsigned SrcSize = TRI->getRegSizeInBits(SrcReg, *MRI); 58604eeddc0SDimitry Andric LLT ResTy = MRI->getType(ResRegs[i]); 58704eeddc0SDimitry Andric if (ResTy.isScalar() && ResTy.getSizeInBits() < SrcSize) { 5885ffd83dbSDimitry Andric // First copy the non-typed virtual register into a generic virtual 5895ffd83dbSDimitry Andric // register 5905ffd83dbSDimitry Andric Register Tmp1Reg = 5915ffd83dbSDimitry Andric MRI->createGenericVirtualRegister(LLT::scalar(SrcSize)); 5925ffd83dbSDimitry Andric MIRBuilder.buildCopy(Tmp1Reg, SrcReg); 5935ffd83dbSDimitry Andric // Need to truncate the result of the register 5945ffd83dbSDimitry Andric MIRBuilder.buildTrunc(ResRegs[i], Tmp1Reg); 59504eeddc0SDimitry Andric } else if (ResTy.getSizeInBits() == SrcSize) { 5965ffd83dbSDimitry Andric MIRBuilder.buildCopy(ResRegs[i], SrcReg); 59704eeddc0SDimitry Andric } else { 59804eeddc0SDimitry Andric LLVM_DEBUG(dbgs() << "Unhandled output operand with " 59904eeddc0SDimitry Andric "mismatched register size\n"); 60004eeddc0SDimitry Andric return false; 6015ffd83dbSDimitry Andric } 60204eeddc0SDimitry Andric 6035ffd83dbSDimitry Andric break; 6045ffd83dbSDimitry Andric } 6055ffd83dbSDimitry Andric case TargetLowering::C_Immediate: 6065ffd83dbSDimitry Andric case TargetLowering::C_Other: 6075ffd83dbSDimitry Andric LLVM_DEBUG( 6085ffd83dbSDimitry Andric dbgs() << "Cannot lower target specific output constraints yet\n"); 6095ffd83dbSDimitry Andric return false; 6105ffd83dbSDimitry Andric case TargetLowering::C_Memory: 6115ffd83dbSDimitry Andric break; // Already handled. 61281ad6265SDimitry Andric case TargetLowering::C_Address: 61381ad6265SDimitry Andric break; // Silence warning. 6145ffd83dbSDimitry Andric case TargetLowering::C_Unknown: 6155ffd83dbSDimitry Andric LLVM_DEBUG(dbgs() << "Unexpected unknown constraint\n"); 6165ffd83dbSDimitry Andric return false; 6175ffd83dbSDimitry Andric } 6185ffd83dbSDimitry Andric } 6195ffd83dbSDimitry Andric 6205ffd83dbSDimitry Andric return true; 6215ffd83dbSDimitry Andric } 6225ffd83dbSDimitry Andric 6235ffd83dbSDimitry Andric bool InlineAsmLowering::lowerAsmOperandForConstraint( 6245ffd83dbSDimitry Andric Value *Val, StringRef Constraint, std::vector<MachineOperand> &Ops, 6255ffd83dbSDimitry Andric MachineIRBuilder &MIRBuilder) const { 6265ffd83dbSDimitry Andric if (Constraint.size() > 1) 6275ffd83dbSDimitry Andric return false; 6285ffd83dbSDimitry Andric 6295ffd83dbSDimitry Andric char ConstraintLetter = Constraint[0]; 6305ffd83dbSDimitry Andric switch (ConstraintLetter) { 6315ffd83dbSDimitry Andric default: 6325ffd83dbSDimitry Andric return false; 6335ffd83dbSDimitry Andric case 'i': // Simple Integer or Relocatable Constant 634e8d8bef9SDimitry Andric case 'n': // immediate integer with a known value. 6355ffd83dbSDimitry Andric if (ConstantInt *CI = dyn_cast<ConstantInt>(Val)) { 6365ffd83dbSDimitry Andric assert(CI->getBitWidth() <= 64 && 6375ffd83dbSDimitry Andric "expected immediate to fit into 64-bits"); 6385ffd83dbSDimitry Andric // Boolean constants should be zero-extended, others are sign-extended 6395ffd83dbSDimitry Andric bool IsBool = CI->getBitWidth() == 1; 6405ffd83dbSDimitry Andric int64_t ExtVal = IsBool ? CI->getZExtValue() : CI->getSExtValue(); 6415ffd83dbSDimitry Andric Ops.push_back(MachineOperand::CreateImm(ExtVal)); 6425ffd83dbSDimitry Andric return true; 6435ffd83dbSDimitry Andric } 6445ffd83dbSDimitry Andric return false; 6455ffd83dbSDimitry Andric } 6465ffd83dbSDimitry Andric } 647