TableGen:指令中嵌套操作数的OperandType

TableGen:指令中嵌套操作数的OperandType

Consider, for example, ARM’s predicate operand:

例如,考虑ARM的谓词操作数:

def pred : PredicateOperand<OtherVT, (ops i32imm, i32imm),

                                     (ops (i32 14), (i32 zero_reg))> {

  let PrintMethod = "printPredicateOperand";

  let ParserMatchClass = CondCodeOperand;

  let DecoderMethod = "DecodePredicateOperand";

}

Or addrmode5

class AddrMode5 : MemOperand,

                  ComplexPattern<i32, 2, "SelectAddrMode5", []> {

  let EncoderMethod = "getAddrMode5OpValue";

  let DecoderMethod = "DecodeAddrMode5Operand";

  let ParserMatchClass = AddrMode5AsmOperand;

  let MIOperandInfo = (ops GPR:$base, i32imm);

}

 

def addrmode5 : AddrMode5 {

   let PrintMethod = "printAddrMode5Operand<false>";

}

例如,考虑ARM的谓词操作数:这些操作数在MIOperandInfo中有多个操作数组成整体。

These operands have multiple operands in MIOperandInfo composing the whole.

  • pred (i32imm, i32imm)
  • addrmode5 (GPR, i32imm)

“i32imm”被定义为OPERAND_IMMEDIATE类型,但InstrInfoEmitter::GetOperandInfo会为展平操作数列表(–gen instr info)中与i32immm关联的索引发出OPERAND_UNKNOWN类型。

这似乎是预期的行为。考虑上面的“pred” ,‘zero_reg’(寄存器)是默认值,但i32imm(立即数)是操作数类型。

‘i32imm’ is defined as type OPERAND_IMMEDIATE, but InstrInfoEmitter::GetOperandInfo emits the type OPERAND_UNKNOWN for the index associated with the i32imm in the flattened operand list (–gen-instr-info).

本希望机器代码验证器在指令中看到$noreg时标记一个错误,其中需要立即数,但因为操作数类型是UNKNOWN而不是immediate,所以跳过了该错误。

再深入一点,发现InstrInfoEmitter.cpp将每个子操作数的类型分配给包含操作数。

This seems to be expected behavior. Consider ‘pred’ above. ‘zero_reg’, a register, is a default value, but i32imm, an immediate, is the operand type. I would have expected the machine code verifier to flag an error when it saw $noreg in the instruction where an immediate is expected, but because the operand type is UNKNOWN instead of IMMEDIATE, the error is skipped.

Digging a bit deeper, I found that InstrInfoEmitter.cpp assigns the type of each sub-operand to the containing operand.

  1. Res += "|(1<<MCOI::OptionalDef)";
  2.  
  3.           
  4. // Branch target operands.  Check to see if the original unexpanded
  5. // operand was of type BranchTargetOperand.
  6. if (Op.Rec->isSubClassOf("BranchTargetOperand"))
  7.   Res += "|(1<<MCOI::BranchTarget)";
  8.  
  9.           
  10. // Fill in operand type.
  11. Res += ", ";
  12. assert(!Op.OperandType.empty() && "Invalid operand type.");
  13. Res += Op.OperandType;
  14.  
  15.           
  16. // Fill in constraint info.
  17. Res += ", ";
  18.  
  19.           
  20. const CGIOperandList::ConstraintInfo &Constraint =
  21.   Op.Constraints[j];
  22. if (Constraint.isNone())
  23.   Res += "0";
  24. else if (Constraint.isEarlyClobber())
  25.   Res += "MCOI_EARLY_CLOBBER";

参考文献链接

llvm/llvm-project/blob/4efcea95852abe6ed25ae9a2bf8c3a51a1157675/llvm/utils/TableGen/InstrInfoEmitter.cpp#L195

Is there an ambiguous situation that is being avoided by such a classification? Assigning ‘correct’ types all the time seems to be a non-starter, as both operands above start break in multiple places during normal machine code verification, such as when addrmode5’s first operand receives a constant pool entry rather than a register for its first operand.

My current experiment replicates some code from CGIOperandList which inspects a record to construct the operand namespace and type.

这种分类是否避免了模棱两可的情况?始终指定“正确”类型似乎不是一个开始,因为在正常的机器代码验证过程中,两个开头以上的操作数都会在多个位置中断,例如addrmode5的第一个操作数收到一个常量池条目,而不是第一个操作数的寄存器。

当前的实验复制了CGIOperandList中的一些代码,该代码检查记录以构造操作数名称空间和类型。

  1. PrintFatalError(R->getLoc(), "Illegal operand for the '" + R->getName() +
  2.                                    "' instruction!");
  3.  
  4.           
  5. Record *Rec = Arg->getDef();
  6. std::string PrintMethod = "printOperand";
  7. std::string EncoderMethod;
  8. std::string OperandType = "OPERAND_UNKNOWN";
  9. std::string OperandNamespace = "MCOI";
  10. unsigned NumOps = 1;
  11. DagInit *MIOpInfo = nullptr;
  12. if (Rec->isSubClassOf("RegisterOperand")) {
  13.   PrintMethod = std::string(Rec->getValueAsString("PrintMethod"));
  14.   OperandType = std::string(Rec->getValueAsString("OperandType"));
  15.   OperandNamespace = std::string(Rec->getValueAsString("OperandNamespace"));
  16.   EncoderMethod = std::string(Rec->getValueAsString("EncoderMethod"));
  17. } else if (Rec->isSubClassOf("Operand")) {
  18.   PrintMethod = std::string(Rec->getValueAsString("PrintMethod"));
  19.   OperandType = std::string(Rec->getValueAsString("OperandType"));
  20.   OperandNamespace = std::string(Rec->getValueAsString("OperandNamespace"));
  21.   // If there is an explicit encoder method, use it.
  22.   EncoderMethod = std::string(Rec->getValueAsString("EncoderMethod"));

参考文献链接

llvm/llvm-project/blob/4efcea95852abe6ed25ae9a2bf8c3a51a1157675/llvm/utils/TableGen/CodeGenInstruction.cpp#L86

参考文献链接

https://discourse.llvm.org/t/tablegen-operandtype-of-nested-operands-in-instructions/67099/1

posted @ 2023-01-01 04:39  吴建明wujianming  阅读(77)  评论(0编辑  收藏  举报