Fix ARM emit() methods to count instructions generated.
Previously, the code assumed that the emit() method of all ARM
instructions emitted a single instruction. This is false. Instructions
like PUSH and POP may generate multiple instructions.
This is only a problem when the hybrid ARM assembler reverts back to
using the stand-alone assembler to generate instructions the
integrated assembler can't handle.
The fix is to add infrastructure to allow ARM instructions to
communicate to the assembler, the number of instructions they
generate, so that the correct-sized filler is added to the assembly
buffer.
This fixes all cross-test failures for (pc-relative) branches, except
one.
BUG= https://bugs.chromium.org/p/nativeclient/issues/detail?id=4334
R=stichnot@chromium.org
Review URL: https://codereview.chromium.org/1426513004 .
diff --git a/src/IceAssemblerARM32.cpp b/src/IceAssemblerARM32.cpp
index 135e1b9..756b0a6 100644
--- a/src/IceAssemblerARM32.cpp
+++ b/src/IceAssemblerARM32.cpp
@@ -270,9 +270,8 @@
size_t MoveRelocatableFixup::emit(GlobalContext *Ctx,
const Assembler &Asm) const {
- static constexpr const size_t FixupSize = sizeof(IValueT);
if (!BuildDefs::dump())
- return FixupSize;
+ return InstARM32::InstSize;
Ostream &Str = Ctx->getStrEmit();
IValueT Inst = Asm.load<IValueT>(position());
Str << "\tmov" << (kind() == llvm::ELF::R_ARM_MOVW_ABS_NC ? "w" : "t") << "\t"
@@ -280,7 +279,7 @@
<< ", #:" << (kind() == llvm::ELF::R_ARM_MOVW_ABS_NC ? "lower" : "upper")
<< "16:" << symbol(Ctx) << "\t@ .word "
<< llvm::format_hex_no_prefix(Inst, 8) << "\n";
- return FixupSize;
+ return InstARM32::InstSize;
}
MoveRelocatableFixup *AssemblerARM32::createMoveFixup(bool IsMovW,
@@ -360,11 +359,12 @@
}
void AssemblerARM32::emitTextInst(const std::string &Text, SizeT InstSize) {
- AssemblerBuffer::EnsureCapacity ensured(&Buffer);
AssemblerFixup *F = createTextFixup(Text, InstSize);
emitFixup(F);
- for (SizeT I = 0; I < InstSize; ++I)
+ for (SizeT I = 0; I < InstSize; ++I) {
+ AssemblerBuffer::EnsureCapacity ensured(&Buffer);
Buffer.emit<char>(0);
+ }
}
void AssemblerARM32::emitType01(CondARM32::Cond Cond, IValueT Type,
@@ -393,10 +393,9 @@
emitType01(Opcode, Rd, Rn, OpSrc1, SetFlags, Cond, RuleChecks);
}
-void AssemblerARM32::emitType01(IValueT Opcode, IValueT Rd,
- IValueT Rn, const Operand *OpSrc1,
- bool SetFlags, CondARM32::Cond Cond,
- Type01Checks RuleChecks) {
+void AssemblerARM32::emitType01(IValueT Opcode, IValueT Rd, IValueT Rn,
+ const Operand *OpSrc1, bool SetFlags,
+ CondARM32::Cond Cond, Type01Checks RuleChecks) {
switch (RuleChecks) {
case NoChecks:
break;
diff --git a/src/IceAssemblerARM32.h b/src/IceAssemblerARM32.h
index 6cd120e..c7ea86b 100644
--- a/src/IceAssemblerARM32.h
+++ b/src/IceAssemblerARM32.h
@@ -82,7 +82,7 @@
SizeT BytesNeeded = Utils::OffsetToAlignment(Buffer.getPosition(), Align);
constexpr IValueT UndefinedInst = 0xe7fedef0; // udf #60896
constexpr SizeT InstSize = sizeof(IValueT);
- assert(BytesNeeded % InstSize == 0);
+ assert(BytesNeeded % InstARM32::InstSize == 0);
while (BytesNeeded > 0) {
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
emitInst(UndefinedInst);
@@ -137,6 +137,14 @@
return false;
}
+ /// Accessors to keep track of the number of bytes generated inside
+ /// InstARM32::emit() methods, when run inside of
+ /// InstARM32::emitUsingTextFixup().
+ void resetEmitTextSize() { EmitTextSize = 0; }
+ void incEmitTextSize(size_t Amount) { EmitTextSize += Amount; }
+ void decEmitTextSize(size_t Amount) { EmitTextSize -= Amount; }
+ size_t getEmitTextSize() const { return EmitTextSize; }
+
void bind(Label *label);
// List of instructions implemented by integrated assembler.
@@ -198,7 +206,7 @@
return Asm->getKind() == Asm_ARM32;
}
- void emitTextInst(const std::string &Text, SizeT InstSize = sizeof(IValueT));
+ void emitTextInst(const std::string &Text, SizeT InstSize);
private:
// A vector of pool-allocated x86 labels for CFG nodes.
@@ -206,6 +214,9 @@
LabelVector CfgNodeLabels;
// A vector of pool-allocated x86 labels for Local labels.
LabelVector LocalLabels;
+ // Number of bytes emitted by InstARM32::emit() methods, when run inside
+ // InstARM32::emitUsingTextFixup().
+ size_t EmitTextSize = 0;
Label *getOrCreateLabel(SizeT Number, LabelVector &Labels);
diff --git a/src/IceInstARM32.cpp b/src/IceInstARM32.cpp
index cd50eb1..b03daf2 100644
--- a/src/IceInstARM32.cpp
+++ b/src/IceInstARM32.cpp
@@ -84,6 +84,11 @@
return InstARM32CondAttributes[Cond].Opposite;
}
+void InstARM32::startNextInst(const Cfg *Func) const {
+ if (auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>())
+ Asm->incEmitTextSize(InstSize);
+}
+
void InstARM32::emitUsingTextFixup(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
@@ -98,9 +103,13 @@
OstreamLocker L(Ctx);
Ostream &OldStr = Ctx->getStrEmit();
Ctx->setStrEmit(StrBuf);
+ // Start counting instructions here, so that emit() methods don't
+ // need to call this for the first instruction.
+ Asm->resetEmitTextSize();
+ Asm->incEmitTextSize(InstSize);
emit(Func);
Ctx->setStrEmit(OldStr);
- Asm->emitTextInst(StrBuf.str());
+ Asm->emitTextInst(StrBuf.str(), Asm->getEmitTextSize());
}
void InstARM32::emitIAS(const Cfg *Func) const { emitUsingTextFixup(Func); }
@@ -810,6 +819,7 @@
} else {
Str << getTargetTrue()->getAsmName();
if (getTargetFalse()) {
+ startNextInst(Func);
Str << "\n\t"
<< "b"
<< "\t" << getTargetFalse()->getAsmName();
@@ -1069,6 +1079,7 @@
for (const Operand *Op : Dests) {
if (isScalarIntegerType(Op->getType()))
continue;
+ startNextInst(Func);
Str << "\t"
<< "vpop"
<< "\t{";
@@ -1139,6 +1150,7 @@
Str << "}\n";
}
if (IntegerCount != 0) {
+ startNextInst(Func);
Str << "\t"
<< "push"
<< "\t{";
diff --git a/src/IceInstARM32.h b/src/IceInstARM32.h
index 28317d2..cf54485 100644
--- a/src/IceInstARM32.h
+++ b/src/IceInstARM32.h
@@ -334,10 +334,18 @@
Vsub
};
+ static constexpr size_t InstSize = sizeof(uint32_t);
+
static const char *getWidthString(Type Ty);
static const char *getVecWidthString(Type Ty);
static CondARM32::Cond getOppositeCondition(CondARM32::Cond Cond);
+ /// Called inside derived methods emit() to communicate that multiple
+ /// instructions are being generated. Used by emitIAS() methods to
+ /// generate textual fixups for instructions that are not yet
+ /// implemented.
+ void startNextInst(const Cfg *Func) const;
+
/// Shared emit routines for common forms of instructions.
static void emitThreeAddrFP(const char *Opcode, const InstARM32 *Inst,
const Cfg *Func);
diff --git a/src/IceTargetLoweringARM32.h b/src/IceTargetLoweringARM32.h
index 7e7e520..22b5c31 100644
--- a/src/IceTargetLoweringARM32.h
+++ b/src/IceTargetLoweringARM32.h
@@ -331,7 +331,8 @@
// _mov_i1_to_flags is used for bool folding. If "Boolean" is folded, this
// method returns true, and sets "CondIfTrue0" and "CondIfTrue1" to the
- // appropriate ARM condition codes. If "Boolean" is not to be folded, then this
+ // appropriate ARM condition codes. If "Boolean" is not to be folded, then
+ // this
// method returns false.
bool _mov_i1_to_flags(Operand *Boolean, CondARM32::Cond *CondIfTrue0,
CondARM32::Cond *CondIfTrue1,