LLVM学习笔记(18)

3.4.2.5. Instruction的处理

CodeGenDAGPatterns下一步开始处理指令定义。在Instruction的定义里有一个list<dag>类型的成员Pattern,如果不是空,它指定了该指令的匹配模式。在3072行获取Pattern的定义。

3065  void CodeGenDAGPatterns::ParseInstructions() {

3066    std::vector<Record*> Instrs = Records.getAllDerivedDefinitions("Instruction");

3067 

3068    for (unsigned i = 0, e = Instrs.size(); i != e; ++i) {

3069      ListInit *LI = nullptr;

3070 

3071      if (isa<ListInit>(Instrs[i]->getValueInit("Pattern")))

3072        LI = Instrs[i]->getValueAsListInit("Pattern");

3073 

3074      // If there is no pattern, only collect minimal information about the

3075      // instruction for its operand list.  We have to assume that there is one

3076      // result, as we have no detailed info. A pattern which references the

3077      // null_frag operator is as-if no pattern were specified. Normally this

3078      // is from a multiclass expansion w/ a SDPatternOperator passed in as

3079      // null_frag.

3080      if (!LI || LI->empty() || hasNullFragReference(LI)) {

3081        std::vector<Record*> Results;

3082        std::vector<Record*> Operands;

3083 

3084        CodeGenInstruction &InstInfo = Target.getInstruction(Instrs[i]);

3085 

3086        if (InstInfo.Operands.size() != 0) {

3087          for (unsigned j = 0, e = InstInfo.Operands.NumDefs; j < e; ++j)

3088            Results.push_back(InstInfo.Operands[j].Rec);

3089 

3090          // The rest are inputs.

3091          for (unsigned j = InstInfo.Operands.NumDefs,

3092                 e = InstInfo.Operands.size(); j < e; ++j)

3093            Operands.push_back(InstInfo.Operands[j].Rec);

3094        }

3095 

3096        // Create and insert the instruction.

3097        std::vector<Record*> ImpResults;

3098        Instructions.insert(std::make_pair(Instrs[i],

3099                            DAGInstruction(nullptr, Results, Operands, ImpResults)));   <-- v7.0删除

                          DAGInstruction(Results, Operands, ImpResults)));                 <-- v7.0增加

3100        continue// no pattern.

3101      }

3102 

3103      CodeGenInstruction &CGI = Target.getInstruction(Instrs[i]);

3104      const DAGInstruction &DI = parseInstructionPattern(CGI, LI, Instructions);

3105 

3106      (void)DI;

3107      DEBUG(DI.getPattern()->dump());

3108    }

3.4.2.5.1. 无匹配模式的Instruction

在TargetSelectionDAG.td文件里有一个null_frag的定义,专用于表示空模式。在一个展开的模式里,一旦援引了null_frag的定义,这个模式将被丢弃。这可以实现可选模式。那么,3080行的hasNullFragReference()在模式中查找是否使用null_frag作为操作符,一旦找到就返回true。

如果Instruction定义没有定义Pattern(使用null_frag作为操作符也等同于没有Pattern),仅需要生成收集最少量参数列表信息的CodeGenInstruction实例。

157     CodeGenInstruction &getInstruction(const Record *InstRec) const {

158         if (Instructions.empty()) ReadInstructions();

159         auto I = Instructions.find(InstRec);

160         assert(I != Instructions.end() && "Not an instruction");

161         return *I->second;

162       }

Instructions是mutable DenseMap<const Record*, std::unique_ptr<CodeGenInstruction>>类型的容器(CodeGenTarget的成员),这时它一定是空的。因此,由下面的方法创建所有Instruction定义所对应的CodeGenInstruction实例。

267     void CodeGenTarget::ReadInstructions() const {

268       std::vector<Record*> Insts = Records.getAllDerivedDefinitions("Instruction");

269       if (Insts.size() <= 2)

270         PrintFatalError("No 'Instruction' subclasses defined!");

271    

272       // Parse the instructions defined in the .td file.

273       for (unsigned i = 0, e = Insts.size(); i != e; ++i)

274         Instructions[Insts[i]] = llvm::make_unique<CodeGenInstruction>(Insts[i]);

275     }

274行调用了CodeGenInstruction的构造函数。

295     CodeGenInstruction::CodeGenInstruction(Record *R)

296       : TheDef(R), Operands(R), InferredFrom(nullptr) {

297       Namespace = R->getValueAsString("Namespace");

298       AsmString = R->getValueAsString("AsmString");

299    

300       isReturn     = R->getValueAsBit("isReturn");

301       isBranch     = R->getValueAsBit("isBranch");

302       isIndirectBranch = R->getValueAsBit("isIndirectBranch");

303       isCompare    = R->getValueAsBit("isCompare");

304       isMoveImm    = R->getValueAsBit("isMoveImm");

  isMoveReg    = R->getValueAsBit("isMoveReg");    <-- v7.0增加

305       isBitcast    = R->getValueAsBit("isBitcast");

306       isSelect     = R->getValueAsBit("isSelect");

307       isBarrier    = R->getValueAsBit("isBarrier");

308       isCall       = R->getValueAsBit("isCall");

  isAdd        = R->getValueAsBit("isAdd");                    <-- v7.0增加

  isTrap       = R->getValueAsBit("isTrap");

309       canFoldAsLoad = R->getValueAsBit("canFoldAsLoad");

310       isPredicable = Operands.isPredicable || R->getValueAsBit("isPredicable");

311       isConvertibleToThreeAddress = R->getValueAsBit("isConvertibleToThreeAddress");

312       isCommutable = R->getValueAsBit("isCommutable");

313       isTerminator = R->getValueAsBit("isTerminator");

314       isReMaterializable = R->getValueAsBit("isReMaterializable");

315       hasDelaySlot = R->getValueAsBit("hasDelaySlot");

316       usesCustomInserter = R->getValueAsBit("usesCustomInserter");

317       hasPostISelHook = R->getValueAsBit("hasPostISelHook");

318       hasCtrlDep   = R->getValueAsBit("hasCtrlDep");

319       isNotDuplicable = R->getValueAsBit("isNotDuplicable");

320       isRegSequence = R->getValueAsBit("isRegSequence");

321       isExtractSubreg = R->getValueAsBit("isExtractSubreg");

322       isInsertSubreg = R->getValueAsBit("isInsertSubreg");

323       isConvergent = R->getValueAsBit("isConvergent");

  FastISelShouldIgnore = R->getValueAsBit("FastISelShouldIgnore");    <-- v7.0增加

324    

325       bool Unset;

326       mayLoad      = R->getValueAsBitOrUnset("mayLoad", Unset);

327       mayLoad_Unset = Unset;

328       mayStore     = R->getValueAsBitOrUnset("mayStore", Unset);

329       mayStore_Unset = Unset;

330       hasSideEffects = R->getValueAsBitOrUnset("hasSideEffects", Unset);

331       hasSideEffects_Unset = Unset;

332    

333       isAsCheapAsAMove = R->getValueAsBit("isAsCheapAsAMove");

334       hasExtraSrcRegAllocReq = R->getValueAsBit("hasExtraSrcRegAllocReq");

335       hasExtraDefRegAllocReq = R->getValueAsBit("hasExtraDefRegAllocReq");

336       isCodeGenOnly = R->getValueAsBit("isCodeGenOnly");

337       isPseudo = R->getValueAsBit("isPseudo");

338       ImplicitDefs = R->getValueAsListOfDefs("Defs");

339       ImplicitUses = R->getValueAsListOfDefs("Uses");

 

  // This flag is only inferred from the pattern.                    <-- v7.0增加

  hasChain = false;

  hasChain_Inferred = false;

340    

341       // Parse Constraints.

342       ParseConstraints(R->getValueAsString("Constraints"), Operands);

343    

344       // Parse the DisableEncoding field.

345       Operands.ProcessDisableEncoding(R->getValueAsString("DisableEncoding"));

346    

347       // First check for a ComplexDeprecationPredicate.

348       if (R->getValue("ComplexDeprecationPredicate")) {

349         HasComplexDeprecationPredicate = true;

350         DeprecatedReason = R->getValueAsString("ComplexDeprecationPredicate");

351       } else if (RecordVal *Dep = R->getValue("DeprecatedFeatureMask")) {

352         // Check if we have a Subtarget feature mask.

353         HasComplexDeprecationPredicate = false;

354         DeprecatedReason = Dep->getValue()->getAsString();

355       } else {

356         // This instruction isn't deprecated.

357         HasComplexDeprecationPredicate = false;

358         DeprecatedReason = "";

359       }

360     }

296行的Operands是CGIOperandList类型的成员,因此在该行调用了CGIOperandList的构造函数,为Instruction定义中的操作数构建OperandInfo实例。

28       CGIOperandList::CGIOperandList(Record *R) : TheDef(R) {

29         isPredicable = false;

30         hasOptionalDef = false;

31         isVariadic = false;

32      

33         DagInit *OutDI = R->getValueAsDag("OutOperandList");

34      

35         if (DefInit *Init = dyn_cast<DefInit>(OutDI->getOperator())) {

36           if (Init->getDef()->getName() != "outs")

37             PrintFatalError(R->getName() + ": invalid def name for output list: use 'outs'");

38         } else

39           PrintFatalError(R->getName() + ": invalid output list: use 'outs'");

40      

41         NumDefs = OutDI->getNumArgs();

42      

43         DagInit *InDI = R->getValueAsDag("InOperandList");

44         if (DefInit *Init = dyn_cast<DefInit>(InDI->getOperator())) {

45           if (Init->getDef()->getName() != "ins")

46             PrintFatalError(R->getName() + ": invalid def name for input list: use 'ins'");

47         } else

48           PrintFatalError(R->getName() + ": invalid input list: use 'ins'");

49      

50         unsigned MIOperandNo = 0;

51         std::set<std::string> OperandNames;

  unsigned e = InDI->getNumArgs() + OutDI->getNumArgs();                                     <-- v7.0增加

  OperandList.reserve(e);

  for (unsigned i = 0; i != e; ++i){

52         for (unsigned i = 0, e = InDI->getNumArgs()+OutDI->getNumArgs(); i != e; ++i){  <-- v7.0删除

53           Init *ArgInit;

54           std::string ArgName;

55           if (i < NumDefs) {

56             ArgInit = OutDI->getArg(i);

57             ArgName = OutDI->getArgName(i);

58           } else {

59             ArgInit = InDI->getArg(i-NumDefs);

60             ArgName = InDI->getArgName(i-NumDefs);

61           }

62      

63           DefInit *Arg = dyn_cast<DefInit>(ArgInit);

64           if (!Arg)

65             PrintFatalError("Illegal operand for the '" + R->getName() + "' instruction!");

66      

67           Record *Rec = Arg->getDef();

68           std::string PrintMethod = "printOperand";

69           std::string EncoderMethod;

70           std::string OperandType = "OPERAND_UNKNOWN";

71           std::string OperandNamespace = "MCOI";

72           unsigned NumOps = 1;

73           DagInit *MIOpInfo = nullptr;

74           if (Rec->isSubClassOf("RegisterOperand")) {

75            PrintMethod = Rec->getValueAsString("PrintMethod");

76             OperandType = Rec->getValueAsString("OperandType");

77             OperandNamespace = Rec->getValueAsString("OperandNamespace");

      EncoderMethod = Rec->getValueAsString("EncoderMethod");                   <-- v7.0增加

78           } else if (Rec->isSubClassOf("Operand")) {

79            PrintMethod = Rec->getValueAsString("PrintMethod");

80             OperandType = Rec->getValueAsString("OperandType");

81             // If there is an explicit encoder method, use it.

82             EncoderMethod = Rec->getValueAsString("EncoderMethod");

83             MIOpInfo = Rec->getValueAsDag("MIOperandInfo");

84      

85             // Verify that MIOpInfo has an 'ops' root value.

86             if (!isa<DefInit>(MIOpInfo->getOperator()) ||

87                 cast<DefInit>(MIOpInfo->getOperator())->getDef()->getName() != "ops")

88               PrintFatalError("Bad value for MIOperandInfo in operand '" + Rec->getName() +

89                 "'\n");

90      

91             // If we have MIOpInfo, then we have #operands equal to number of entries

92             // in MIOperandInfo.

93             if (unsigned NumArgs = MIOpInfo->getNumArgs())

94               NumOps = NumArgs;

95      

96             if (Rec->isSubClassOf("PredicateOp"))

97               isPredicable = true;

98             else if (Rec->isSubClassOf("OptionalDefOperand"))

99               hasOptionalDef = true;

100         } else if (Rec->getName() == "variable_ops") {

101           isVariadic = true;

102           continue;

103         } else if (Rec->isSubClassOf("RegisterClass")) {

104           OperandType = "OPERAND_REGISTER";

105         } else if (!Rec->isSubClassOf("PointerLikeRegClass") &&

106                    !Rec->isSubClassOf("unknown_class"))

107           PrintFatalError("Unknown operand class '" + Rec->getName() +

108             "' in '" + R->getName() + "' instruction!");

109    

110       // Check that the operand has a name and that it's unique.

111         if (ArgName.empty())

112           PrintFatalError("In instruction '" + R->getName() + "', operand #" +

113                           Twine(i) + " has no name!");

114         if (!OperandNames.insert(ArgName).second)

115           PrintFatalError("In instruction '" + R->getName() + "', operand #" +

116                           Twine(i) + " has the same name as a previous operand!");

117    

118         OperandList.emplace_back(Rec, ArgName, PrintMethod, EncoderMethod,

119                                  OperandNamespace + "::" + OperandType, MIOperandNo,

120                                  NumOps, MIOpInfo);

121         MIOperandNo += NumOps;

122       }

123    

124    

125       // Make sure the constraints list for each operand is large enough to hold

126       // constraint info, even if none is present.

127       for (unsigned i = 0, e = OperandList.size(); i != e; ++i)

128         OperandList[i].Constraints.resize(OperandList[i].MINumOperands);

129     }

上面的Record实例的getValue*方法通过参数指定要访问的成员名,*部分则指明了成员的类型,这些方法将返回对应类型的成员值。

首先,确定Instruction定义中的OutOperandList与InOperandList分别是以out及ins作为操作符的dag。在52行的循环里依次访问OutOperandList与InOperandList的操作数。可以作为输入、输出操作数的类型有:RegisterOperand,Operand,RegisterClass,variable_ops,PointerLikeRegClass。其中部分的TD定义分别是:

144     class DAGOperand { }

 

519     def variable_ops;

 

526     class PointerLikeRegClass<int Kind> {

526       int RegClassKind = Kind;

528     }

构建了CGIOperandList实例后,要解析对这些操作数限定的描述。在CodeGenInstruction构造函数的342行通过ParseConstraints()方法进行解析这些描述(TD的Instruction定义里的Constraints域)。

256     static void ParseConstraints(const std::string &CStr, CGIOperandList &Ops) {

257       if (CStr.empty()) return;

258    

259       const std::string delims(",");

260       std::string::size_type bidx, eidx;

261    

262       bidx = CStr.find_first_not_of(delims);

263       while (bidx != std::string::npos) {

264         eidx = CStr.find_first_of(delims, bidx);

265         if (eidx == std::string::npos)

266           eidx = CStr.length();

267    

268         ParseConstraint(CStr.substr(bidx, eidx - bidx), Ops);

269         bidx = CStr.find_first_not_of(delims, eidx);

270       }

271     }

Constraints字符串可以描述多个限定,它们由字符“,”分割。因此263行的循环找出每个限定描述字符串,并由下面的方法进行分析。

201     static void ParseConstraint(const std::string &CStr, CGIOperandList &Ops) {

202       // EARLY_CLOBBER: @early $reg

203       std::string::size_type wpos = CStr.find_first_of(" \t");

204       std::string::size_type start = CStr.find_first_not_of(" \t");

205       std::string Tok = CStr.substr(start, wpos - start);

206       if (Tok == "@earlyclobber") {

207         std::string Name = CStr.substr(wpos+1);

208         wpos = Name.find_first_not_of(" \t");

209         if (wpos == std::string::npos)

210           PrintFatalError("Illegal format for @earlyclobber constraint: '" + CStr + "'");

211         Name = Name.substr(wpos);

212         std::pair<unsigned,unsigned> Op = Ops.ParseOperandName(Name, false);

213    

214         // Build the string for the operand

215         if (!Ops[Op.first].Constraints[Op.second].isNone())

216           PrintFatalError("Operand '" + Name + "' cannot have multiple constraints!");

217         Ops[Op.first].Constraints[Op.second] =

218         CGIOperandList::ConstraintInfo::getEarlyClobber();

219         return;

220       }

222       // Only other constraint is "TIED_TO" for now.

223       std::string::size_type pos = CStr.find_first_of('=');

224       assert(pos != std::string::npos && "Unrecognized constraint");

225       start = CStr.find_first_not_of(" \t");

226       std::string Name = CStr.substr(start, pos - start);

227    

228       // TIED_TO: $src1 = $dst

229       wpos = Name.find_first_of(" \t");

230       if (wpos == std::string::npos)

231         PrintFatalError("Illegal format for tied-to constraint: '" + CStr + "'");

232       std::string DestOpName = Name.substr(0, wpos);

233       std::pair<unsigned,unsigned> DestOp = Ops.ParseOperandName(DestOpName, false);

234    

235       Name = CStr.substr(pos+1);

236       wpos = Name.find_first_not_of(" \t");

237       if (wpos == std::string::npos)

238         PrintFatalError("Illegal format for tied-to constraint: '" + CStr + "'");

239    

240       std::string SrcOpName = Name.substr(wpos);

241       std::pair<unsigned,unsigned> SrcOp = Ops.ParseOperandName(SrcOpName, false);

242       if (SrcOp > DestOp) {

243         std::swap(SrcOp, DestOp);

244         std::swap(SrcOpName, DestOpName);

245       }

246    

247       unsigned FlatOpNo = Ops.getFlattenedOperandNumber(SrcOp);

248    

249     if (!Ops[DestOp.first].Constraints[DestOp.second].isNone())

250         PrintFatalError("Operand '" + DestOpName +

251           "' cannot have multiple constraints!");

252       Ops[DestOp.first].Constraints[DestOp.second] =

253         CGIOperandList::ConstraintInfo::getTied(FlatOpNo);

254     }

有两种约束形式。一种是@earlyclobber $reg,表示寄存器reg的内容很早就被破坏了。另一种就是所谓的绑定(TIED_TO)约束,诸如$src = $dst的形式。但无论何种约束形式,都要识别出操作数的名字。

156     std::pair<unsigned,unsigned>

157     CGIOperandList::ParseOperandName(const std::string &Op, bool AllowWholeOp) {

158       if (Op.empty() || Op[0] != '$')

159         PrintFatalError(TheDef->getName() + ": Illegal operand name: '" + Op + "'");

160    

161       std::string OpName = Op.substr(1);

162       std::string SubOpName;

163    

164       // Check to see if this is $foo.bar.

165       std::string::size_type DotIdx = OpName.find_first_of(".");

166       if (DotIdx != std::string::npos) {

167         SubOpName = OpName.substr(DotIdx+1);

168         if (SubOpName.empty())

169           PrintFatalError(TheDef->getName() + ": illegal empty suboperand name in '" +Op +"'");

170         OpName = OpName.substr(0, DotIdx);

171       }

172    

173       unsigned OpIdx = getOperandNamed(OpName);

174    

175       if (SubOpName.empty()) {  // If no suboperand name was specified:

176         // If one was needed, throw.

177         if (OperandList[OpIdx].MINumOperands > 1 && !AllowWholeOp &&

178             SubOpName.empty())

179           PrintFatalError(TheDef->getName() + ": Illegal to refer to"

180             " whole operand part of complex operand '" + Op + "'");

181    

182         // Otherwise, return the operand.

183         return std::make_pair(OpIdx, 0U);

184       }

185    

186       // Find the suboperand number involved.

187       DagInit *MIOpInfo = OperandList[OpIdx].MIOperandInfo;

188       if (!MIOpInfo)

189         PrintFatalError(TheDef->getName() + ": unknown suboperand name in '" + Op + "'");

190    

191       // Find the operand with the right name.

192       for (unsigned i = 0, e = MIOpInfo->getNumArgs(); i != e; ++i)

193         if (MIOpInfo->getArgName(i) == SubOpName)

194           return std::make_pair(OpIdx, i);

195    

196       // Otherwise, didn't find it!

197       PrintFatalError(TheDef->getName() + ": unknown suboperand name in '" + Op + "'");

198       return std::make_pair(0U, 0U);

199     }

操作数名字必须以$开始,这个名字可以是类似于$foo.bar这样的形式。对这样的名字,foo部分称为操作数,bar部分称为子操作数(参考ssmem定义)。操作数必须在输入或输出操作数列表里出现,这个列表已经被保存到OperandList容器里了,因此只需要操作数在容器里的索引。

136     unsigned CGIOperandList::getOperandNamed(StringRef Name) const {

137       unsigned OpIdx;

138       if (hasOperandNamed(Name, OpIdx)) return OpIdx;

139       PrintFatalError("'" + TheDef->getName() +

140                       "' does not have an operand named '$" + Name + "'!");

141     }

 

146     bool CGIOperandList::hasOperandNamed(StringRef Name, unsigned &OpIdx) const {

147       assert(!Name.empty() && "Cannot search for operand with no name!");

148       for (unsigned i = 0, e = OperandList.size(); i != e; ++i)

149         if (OperandList[i].Name == Name) {

150           OpIdx = i;

151           return true;

152         }

153       return false;

154     }

如果有多个子操作数,还必须明确地给出子操作数。操作数的索引与子操作数在操作数中的索引作为一个std::pair返回给ParseConstraint。

在215行,Ops是传入的CGIOperandList实例的引用,CGIOperandList重载了[]操作符,返回OperandList容器指定索引处的OperandInfo实例。同一行的Constraints是OperandInfo中类型为std::vector<ConstraintInfo>的容器,而getEarlyClobber()返回一个ConstraintInfo实例来记录这个EarlyClobber约束。:

33           class ConstraintInfo {

34             enum { None, EarlyClobber, Tied } Kind;

35             unsigned OtherTiedOperand;

36           public:

37             ConstraintInfo() : Kind(None) {}

38      

39             static ConstraintInfo getEarlyClobber() {

40               ConstraintInfo I;

41               I.Kind = EarlyClobber;

42               I.OtherTiedOperand = 0;

43               return I;

44             }

45      

46             static ConstraintInfo getTied(unsigned Op) {

47               ConstraintInfo I;

48               I.Kind = Tied;

49               I.OtherTiedOperand = Op;

50               return I;

51             }

同样在253行,getTied()方法返回记录绑定约束的ConstraintInfo实例。不过,由于可能涉及子操作数,在将索引交给getTied之前,需要一个方式将操作数与子操作数的索引编码。

179         unsigned getFlattenedOperandNumber(std::pair<unsigned,unsigned> Op) const {

180           return OperandList[Op.first].MIOperandNo + Op.second;

181         }

这实际上就是LLVM的MachineInstr给操作数编号的方式——把子操作数一字排开。

回到CodeGenInstruction的构造函数。Instruction定义中的DisableEncoding成员列出了不能编码在输出的MachineInstr的操作数,需要通过OperandInfo类型为std::vector<bool>的容器DoNotEncode把它们标记出来。

273     void CGIOperandList::ProcessDisableEncoding(std::string DisableEncoding) {

274       while (1) {

275         std::pair<StringRef, StringRef> P = getToken(DisableEncoding, " ,\t");

276         std::string OpName = P.first;

277         DisableEncoding = P.second;

278         if (OpName.empty()) break;

279    

280         // Figure out which operand this is.

281         std::pair<unsigned,unsigned> Op = ParseOperandName(OpName, false);

282    

283         // Mark the operand as not-to-be encoded.

284         if (Op.second >= OperandList[Op.first].DoNotEncode.size())

285           OperandList[Op.first].DoNotEncode.resize(Op.second+1);

286         OperandList[Op.first].DoNotEncode[Op.second] = true;

287       }

288    

289     }

最后,如果指令被弃用,其Instruction定义还必须继承一个特殊类ComplexDeprecationPredicate(Target.td)。

1110  class ComplexDeprecationPredicate<string dep> {

1111    string ComplexDeprecationPredicate = dep;

1112  }

参数dep用于指定进行启用指令检测的函数名,比如(ARMInstrInfo.td):

5715  // 'it' blocks in ARM mode just validate the predicates. The IT itself

5716  // is discarded.

5717  def ITasm : ARMAsmPseudo<"it$mask $cc", (ins it_pred:$cc, it_mask:$mask)>,

5718           ComplexDeprecationPredicate<"IT">;

在对应的MCInstrDesc实例里将会援引函数getITDeprecationInfo()来进行检启用指令测。因此,这里将这个字符串保存到CodeGenInstruction的成员DeprecatedReason里。

回到CodeGenDAGPatterns::ParseInstructions(),3087行的InstInfo.Operands.NumDefs是这条指令的输出操作数的个数(在前面处理的时候,先处理输出操作数,再是输入操作数)。因此,临时容器Results与Operands分别保存输出与输入操作数,而ImpResults则保存隐含的结果。在3098行,因为没有模式,在调用DAGInstruction构造函数时传入的第一个参数为null(v7.0DAGInstruction定义里去掉了Pattern指针成员,故构造函数不再需要原来的第一个参数)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值