2010-02-10 05:50:41 +08:00
|
|
|
//===- AsmWriterInst.h - Classes encapsulating a printable inst -----------===//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2010-02-10 05:50:41 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// These classes implement a parser for assembly strings.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "AsmWriterInst.h"
|
|
|
|
#include "CodeGenTarget.h"
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2012-10-26 04:33:17 +08:00
|
|
|
#include "llvm/TableGen/Error.h"
|
2011-10-02 00:41:13 +08:00
|
|
|
#include "llvm/TableGen/Record.h"
|
2010-02-10 05:50:41 +08:00
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
static bool isIdentChar(char C) {
|
|
|
|
return (C >= 'a' && C <= 'z') ||
|
|
|
|
(C >= 'A' && C <= 'Z') ||
|
|
|
|
(C >= '0' && C <= '9') ||
|
|
|
|
C == '_';
|
|
|
|
}
|
|
|
|
|
2016-01-14 14:15:07 +08:00
|
|
|
std::string AsmWriterOperand::getCode(bool PassSubtarget) const {
|
2010-02-10 05:50:41 +08:00
|
|
|
if (OperandType == isLiteralTextOperand) {
|
|
|
|
if (Str.size() == 1)
|
2016-01-11 10:11:36 +08:00
|
|
|
return "O << '" + Str + "';";
|
|
|
|
return "O << \"" + Str + "\";";
|
2010-02-10 05:50:41 +08:00
|
|
|
}
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
if (OperandType == isLiteralStatementOperand)
|
|
|
|
return Str;
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
std::string Result = Str + "(MI";
|
[MCInstPrinter] Pass `Address` parameter to MCOI::OPERAND_PCREL typed operands. NFC
Follow-up of D72172 and D72180
This patch passes `uint64_t Address` to print methods of PC-relative
operands so that subsequent target specific patches can change
`*InstPrinter::print{Operand,PCRelImm,...}` to customize the output.
Add MCInstPrinter::PrintBranchImmAsAddress which is set to true by
llvm-objdump.
```
// Current llvm-objdump -d output
aarch64: 20000: bl #0
ppc: 20000: bl .+4
x86: 20000: callq 0
// Ideal output
aarch64: 20000: bl 0x20000
ppc: 20000: bl 0x20004
x86: 20000: callq 0x20005
// GNU objdump -d. The lack of 0x is not ideal because the result cannot be re-assembled
aarch64: 20000: bl 20000
ppc: 20000: bl 0x20004
x86: 20000: callq 20005
```
In `lib/Target/X86/X86GenAsmWriter1.inc` (generated by `llvm-tblgen -gen-asm-writer`):
```
case 12:
// CALL64pcrel32, CALLpcrel16, CALLpcrel32, EH_SjLj_Setup, JCXZ, JECXZ, J...
- printPCRelImm(MI, 0, O);
+ printPCRelImm(MI, Address, 0, O);
return;
```
Some targets have 2 `printOperand` overloads, one without `Address` and
one with `Address`. They should annotate derived `Operand` properly with
`let OperandType = "OPERAND_PCREL"`.
Reviewed By: jhenderson
Differential Revision: https://reviews.llvm.org/D76574
2020-03-23 03:32:27 +08:00
|
|
|
if (PCRel)
|
|
|
|
Result += ", Address";
|
2010-02-10 05:50:41 +08:00
|
|
|
if (MIOpNo != ~0U)
|
|
|
|
Result += ", " + utostr(MIOpNo);
|
2015-03-28 04:36:02 +08:00
|
|
|
if (PassSubtarget)
|
|
|
|
Result += ", STI";
|
2010-04-04 12:47:45 +08:00
|
|
|
Result += ", O";
|
2010-02-10 05:50:41 +08:00
|
|
|
if (!MiModifier.empty())
|
|
|
|
Result += ", \"" + MiModifier + '"';
|
2016-01-11 10:11:36 +08:00
|
|
|
return Result + ");";
|
2010-02-10 05:50:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// ParseAsmString - Parse the specified Instruction's AsmString into this
|
|
|
|
/// AsmWriterInst.
|
|
|
|
///
|
2016-01-17 16:05:33 +08:00
|
|
|
AsmWriterInst::AsmWriterInst(const CodeGenInstruction &CGI, unsigned CGIIndex,
|
|
|
|
unsigned Variant)
|
|
|
|
: CGI(&CGI), CGIIndex(CGIIndex) {
|
2013-07-23 14:25:00 +08:00
|
|
|
|
|
|
|
// NOTE: Any extensions to this code need to be mirrored in the
|
2010-02-10 05:50:41 +08:00
|
|
|
// AsmPrinter::printInlineAsm code that executes as compile time (assuming
|
|
|
|
// that inline asm strings should also get the new feature)!
|
2010-11-01 09:07:14 +08:00
|
|
|
std::string AsmString = CGI.FlattenAsmStringVariants(CGI.AsmString, Variant);
|
2010-02-10 05:50:41 +08:00
|
|
|
std::string::size_type LastEmitted = 0;
|
|
|
|
while (LastEmitted != AsmString.size()) {
|
|
|
|
std::string::size_type DollarPos =
|
2010-11-01 09:07:14 +08:00
|
|
|
AsmString.find_first_of("$\\", LastEmitted);
|
2010-02-10 05:50:41 +08:00
|
|
|
if (DollarPos == std::string::npos) DollarPos = AsmString.size();
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
// Emit a constant string fragment.
|
|
|
|
if (DollarPos != LastEmitted) {
|
2010-11-01 09:07:14 +08:00
|
|
|
for (; LastEmitted != DollarPos; ++LastEmitted)
|
|
|
|
switch (AsmString[LastEmitted]) {
|
|
|
|
case '\n':
|
|
|
|
AddLiteralString("\\n");
|
|
|
|
break;
|
|
|
|
case '\t':
|
2013-12-02 13:10:04 +08:00
|
|
|
AddLiteralString("\\t");
|
2010-11-01 09:07:14 +08:00
|
|
|
break;
|
|
|
|
case '"':
|
|
|
|
AddLiteralString("\\\"");
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
AddLiteralString("\\\\");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
AddLiteralString(std::string(1, AsmString[LastEmitted]));
|
|
|
|
break;
|
|
|
|
}
|
2010-02-10 05:50:41 +08:00
|
|
|
} else if (AsmString[DollarPos] == '\\') {
|
2010-11-01 09:07:14 +08:00
|
|
|
if (DollarPos+1 != AsmString.size()) {
|
2010-02-10 05:50:41 +08:00
|
|
|
if (AsmString[DollarPos+1] == 'n') {
|
|
|
|
AddLiteralString("\\n");
|
|
|
|
} else if (AsmString[DollarPos+1] == 't') {
|
2013-12-02 13:10:04 +08:00
|
|
|
AddLiteralString("\\t");
|
2013-07-23 14:25:00 +08:00
|
|
|
} else if (std::string("${|}\\").find(AsmString[DollarPos+1])
|
2010-02-10 05:50:41 +08:00
|
|
|
!= std::string::npos) {
|
|
|
|
AddLiteralString(std::string(1, AsmString[DollarPos+1]));
|
|
|
|
} else {
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(
|
|
|
|
CGI.TheDef->getLoc(),
|
|
|
|
"Non-supported escaped character found in instruction '" +
|
|
|
|
CGI.TheDef->getName() + "'!");
|
2010-02-10 05:50:41 +08:00
|
|
|
}
|
|
|
|
LastEmitted = DollarPos+2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (DollarPos+1 != AsmString.size() &&
|
|
|
|
AsmString[DollarPos+1] == '$') {
|
2010-11-01 09:07:14 +08:00
|
|
|
AddLiteralString("$"); // "$$" -> $
|
2010-02-10 05:50:41 +08:00
|
|
|
LastEmitted = DollarPos+2;
|
|
|
|
} else {
|
|
|
|
// Get the name of the variable.
|
|
|
|
std::string::size_type VarEnd = DollarPos+1;
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
// handle ${foo}bar as $foo by detecting whether the character following
|
|
|
|
// the dollar sign is a curly brace. If so, advance VarEnd and DollarPos
|
|
|
|
// so the variable name does not contain the leading curly brace.
|
|
|
|
bool hasCurlyBraces = false;
|
|
|
|
if (VarEnd < AsmString.size() && '{' == AsmString[VarEnd]) {
|
|
|
|
hasCurlyBraces = true;
|
|
|
|
++DollarPos;
|
|
|
|
++VarEnd;
|
|
|
|
}
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd]))
|
|
|
|
++VarEnd;
|
2016-01-17 16:47:02 +08:00
|
|
|
StringRef VarName(AsmString.data()+DollarPos+1, VarEnd-DollarPos-1);
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
// Modifier - Support ${foo:modifier} syntax, where "modifier" is passed
|
|
|
|
// into printOperand. Also support ${:feature}, which is passed into
|
|
|
|
// PrintSpecial.
|
|
|
|
std::string Modifier;
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
// In order to avoid starting the next string at the terminating curly
|
|
|
|
// brace, advance the end position past it if we found an opening curly
|
|
|
|
// brace.
|
|
|
|
if (hasCurlyBraces) {
|
|
|
|
if (VarEnd >= AsmString.size())
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(
|
|
|
|
CGI.TheDef->getLoc(),
|
|
|
|
"Reached end of string before terminating curly brace in '" +
|
|
|
|
CGI.TheDef->getName() + "'");
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
// Look for a modifier string.
|
|
|
|
if (AsmString[VarEnd] == ':') {
|
|
|
|
++VarEnd;
|
|
|
|
if (VarEnd >= AsmString.size())
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(
|
|
|
|
CGI.TheDef->getLoc(),
|
|
|
|
"Reached end of string before terminating curly brace in '" +
|
|
|
|
CGI.TheDef->getName() + "'");
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2016-01-17 16:47:02 +08:00
|
|
|
std::string::size_type ModifierStart = VarEnd;
|
2010-02-10 05:50:41 +08:00
|
|
|
while (VarEnd < AsmString.size() && isIdentChar(AsmString[VarEnd]))
|
|
|
|
++VarEnd;
|
|
|
|
Modifier = std::string(AsmString.begin()+ModifierStart,
|
|
|
|
AsmString.begin()+VarEnd);
|
|
|
|
if (Modifier.empty())
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(CGI.TheDef->getLoc(),
|
|
|
|
"Bad operand modifier name in '" +
|
|
|
|
CGI.TheDef->getName() + "'");
|
2010-02-10 05:50:41 +08:00
|
|
|
}
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
if (AsmString[VarEnd] != '}')
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(
|
|
|
|
CGI.TheDef->getLoc(),
|
|
|
|
"Variable name beginning with '{' did not end with '}' in '" +
|
|
|
|
CGI.TheDef->getName() + "'");
|
2010-02-10 05:50:41 +08:00
|
|
|
++VarEnd;
|
|
|
|
}
|
|
|
|
if (VarName.empty() && Modifier.empty())
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(CGI.TheDef->getLoc(),
|
|
|
|
"Stray '$' in '" + CGI.TheDef->getName() +
|
|
|
|
"' asm string, maybe you want $$?");
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
if (VarName.empty()) {
|
|
|
|
// Just a modifier, pass this into PrintSpecial.
|
2016-01-22 13:59:37 +08:00
|
|
|
Operands.emplace_back("PrintSpecial", ~0U, Modifier);
|
2010-02-10 05:50:41 +08:00
|
|
|
} else {
|
|
|
|
// Otherwise, normal operand.
|
2010-11-01 12:03:32 +08:00
|
|
|
unsigned OpNo = CGI.Operands.getOperandNamed(VarName);
|
|
|
|
CGIOperandList::OperandInfo OpInfo = CGI.Operands[OpNo];
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-11-01 09:07:14 +08:00
|
|
|
unsigned MIOp = OpInfo.MIOperandNo;
|
[MCInstPrinter] Pass `Address` parameter to MCOI::OPERAND_PCREL typed operands. NFC
Follow-up of D72172 and D72180
This patch passes `uint64_t Address` to print methods of PC-relative
operands so that subsequent target specific patches can change
`*InstPrinter::print{Operand,PCRelImm,...}` to customize the output.
Add MCInstPrinter::PrintBranchImmAsAddress which is set to true by
llvm-objdump.
```
// Current llvm-objdump -d output
aarch64: 20000: bl #0
ppc: 20000: bl .+4
x86: 20000: callq 0
// Ideal output
aarch64: 20000: bl 0x20000
ppc: 20000: bl 0x20004
x86: 20000: callq 0x20005
// GNU objdump -d. The lack of 0x is not ideal because the result cannot be re-assembled
aarch64: 20000: bl 20000
ppc: 20000: bl 0x20004
x86: 20000: callq 20005
```
In `lib/Target/X86/X86GenAsmWriter1.inc` (generated by `llvm-tblgen -gen-asm-writer`):
```
case 12:
// CALL64pcrel32, CALLpcrel16, CALLpcrel32, EH_SjLj_Setup, JCXZ, JECXZ, J...
- printPCRelImm(MI, 0, O);
+ printPCRelImm(MI, Address, 0, O);
return;
```
Some targets have 2 `printOperand` overloads, one without `Address` and
one with `Address`. They should annotate derived `Operand` properly with
`let OperandType = "OPERAND_PCREL"`.
Reviewed By: jhenderson
Differential Revision: https://reviews.llvm.org/D76574
2020-03-23 03:32:27 +08:00
|
|
|
Operands.emplace_back(OpInfo.PrinterMethodName, MIOp, Modifier,
|
|
|
|
AsmWriterOperand::isMachineInstrOperand,
|
|
|
|
OpInfo.OperandType == "MCOI::OPERAND_PCREL");
|
2010-02-10 05:50:41 +08:00
|
|
|
}
|
|
|
|
LastEmitted = VarEnd;
|
|
|
|
}
|
|
|
|
}
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2015-05-30 03:43:39 +08:00
|
|
|
Operands.emplace_back("return;", AsmWriterOperand::isLiteralStatementOperand);
|
2010-02-10 05:50:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// MatchesAllButOneOp - If this instruction is exactly identical to the
|
|
|
|
/// specified instruction except for one differing operand, return the differing
|
|
|
|
/// operand number. If more than one operand mismatches, return ~1, otherwise
|
|
|
|
/// if the instructions are identical return ~0.
|
|
|
|
unsigned AsmWriterInst::MatchesAllButOneOp(const AsmWriterInst &Other)const{
|
|
|
|
if (Operands.size() != Other.Operands.size()) return ~1;
|
2013-07-23 14:25:00 +08:00
|
|
|
|
2010-02-10 05:50:41 +08:00
|
|
|
unsigned MismatchOperand = ~0U;
|
|
|
|
for (unsigned i = 0, e = Operands.size(); i != e; ++i) {
|
|
|
|
if (Operands[i] != Other.Operands[i]) {
|
|
|
|
if (MismatchOperand != ~0U) // Already have one mismatch?
|
|
|
|
return ~1U;
|
2013-07-23 14:27:36 +08:00
|
|
|
MismatchOperand = i;
|
2010-02-10 05:50:41 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return MismatchOperand;
|
2010-02-10 07:06:35 +08:00
|
|
|
}
|