First pass at emitIAS for branches and binding labels
Currently not testing fixups of forward branches and
instead streaming a ".byte (foo - (. + 1))" or
".long (foo - (. + 4))". It should be supported once
emitIAS() delays writing things out until after the
function is fully emitted (and therefore forward labels
have all been bound).
BUG=none
R=stichnot@chromium.org
Review URL: https://codereview.chromium.org/673543002
diff --git a/src/IceCfg.h b/src/IceCfg.h
index 414a8e1..606f785 100644
--- a/src/IceCfg.h
+++ b/src/IceCfg.h
@@ -94,7 +94,7 @@
template <typename T> T *getAssembler() const {
return static_cast<T *>(TargetAssembler.get());
}
- bool UseIntegratedAssembler() const {
+ bool useIntegratedAssembler() const {
return getContext()->getFlags().UseIntegratedAssembler;
}
bool hasComputedFrame() const;
diff --git a/src/IceCfgNode.cpp b/src/IceCfgNode.cpp
index 4e4d36b..d715767 100644
--- a/src/IceCfgNode.cpp
+++ b/src/IceCfgNode.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
+#include "assembler.h"
#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceInst.h"
@@ -491,6 +492,10 @@
Str << Func->getContext()->mangleName(Func->getFunctionName()) << ":\n";
}
Str << getAsmName() << ":\n";
+ if (Func->useIntegratedAssembler()) {
+ Assembler *Asm = Func->getAssembler<Assembler>();
+ Asm->BindCfgNodeLabel(getIndex());
+ }
for (InstPhi *Phi : Phis) {
if (Phi->isDeleted())
continue;
@@ -505,7 +510,7 @@
// suppress them.
if (I->isRedundantAssign())
continue;
- if (Func->UseIntegratedAssembler()) {
+ if (Func->useIntegratedAssembler()) {
I->emitIAS(Func);
} else {
I->emit(Func);
diff --git a/src/IceInstX8632.cpp b/src/IceInstX8632.cpp
index 9419866..ae4a30d 100644
--- a/src/IceInstX8632.cpp
+++ b/src/IceInstX8632.cpp
@@ -373,6 +373,33 @@
}
}
+void emitIASBytesBranch(const Cfg *Func, const x86::AssemblerX86 *Asm,
+ intptr_t StartPosition, const x86::Label *Label,
+ const IceString &LabelName, bool Near) {
+ // If this is a backward branch (label is bound), we're good and know
+ // the offset. If this is a forward branch, then we can't actually emit
+ // the thing as text in a streaming manner, because the fixup hasn't
+ // happened yet. Instead, emit .long ($BranchLabel) - (. + 4), in that
+ // case and let the external assembler take care of that fixup.
+ if (Label->IsBound()) {
+ emitIASBytes(Func, Asm, StartPosition);
+ return;
+ }
+ const intptr_t FwdBranchSize = Near ? 1 : 4;
+ const IceString FwdBranchDirective = Near ? ".byte" : ".long";
+ Ostream &Str = Func->getContext()->getStrEmit();
+ intptr_t EndPosition = Asm->GetPosition();
+ assert(EndPosition - StartPosition > FwdBranchSize);
+ for (intptr_t i = StartPosition; i < EndPosition - FwdBranchSize; ++i) {
+ Str << "\t.byte 0x";
+ Str.write_hex(Asm->LoadBuffer<uint8_t>(i));
+ Str << "\n";
+ }
+ Str << "\t" << FwdBranchDirective << " " << LabelName << " - (. + "
+ << FwdBranchSize << ")\n";
+ return;
+}
+
} // end of anonymous namespace
void InstX8632::dump(const Cfg *Func) const {
@@ -386,6 +413,15 @@
Str << getName(Func) << ":\n";
}
+void InstX8632Label::emitIAS(const Cfg *Func) const {
+ x86::AssemblerX86 *Asm = Func->getAssembler<x86::AssemblerX86>();
+ Asm->BindLocalLabel(Number);
+ // TODO(jvoung): remove the the textual label once forward branch
+ // fixups are used (and text assembler is not used).
+ Ostream &Str = Func->getContext()->getStrEmit();
+ Str << getName(Func) << ":\n";
+}
+
void InstX8632Label::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << getName(Func) << ":";
@@ -415,6 +451,47 @@
}
}
+void InstX8632Br::emitIAS(const Cfg *Func) const {
+ x86::AssemblerX86 *Asm = Func->getAssembler<x86::AssemblerX86>();
+ intptr_t StartPosition = Asm->GetPosition();
+ if (Label) {
+ x86::Label *L = Asm->GetOrCreateLocalLabel(Label->getNumber());
+ // In all these cases, local Labels should only be used for Near.
+ const bool Near = true;
+ if (Condition == CondX86::Br_None) {
+ Asm->jmp(L, Near);
+ } else {
+ Asm->j(Condition, L, Near);
+ }
+ emitIASBytesBranch(Func, Asm, StartPosition, L, Label->getName(Func), Near);
+ } else {
+ // Pessimistically assume it's far. This only affects Labels that
+ // are not Bound.
+ const bool Near = false;
+ if (Condition == CondX86::Br_None) {
+ x86::Label *L =
+ Asm->GetOrCreateCfgNodeLabel(getTargetFalse()->getIndex());
+ assert(!getTargetTrue());
+ Asm->jmp(L, Near);
+ emitIASBytesBranch(Func, Asm, StartPosition, L,
+ getTargetFalse()->getAsmName(), Near);
+ } else {
+ x86::Label *L = Asm->GetOrCreateCfgNodeLabel(getTargetTrue()->getIndex());
+ Asm->j(Condition, L, Near);
+ emitIASBytesBranch(Func, Asm, StartPosition, L,
+ getTargetTrue()->getAsmName(), Near);
+ StartPosition = Asm->GetPosition();
+ if (getTargetFalse()) {
+ x86::Label *L2 =
+ Asm->GetOrCreateCfgNodeLabel(getTargetFalse()->getIndex());
+ Asm->jmp(L2, Near);
+ emitIASBytesBranch(Func, Asm, StartPosition, L2,
+ getTargetFalse()->getAsmName(), Near);
+ }
+ }
+ }
+}
+
void InstX8632Br::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << "br ";
diff --git a/src/IceInstX8632.h b/src/IceInstX8632.h
index 8235d1f..81d3fd8 100644
--- a/src/IceInstX8632.h
+++ b/src/IceInstX8632.h
@@ -274,13 +274,14 @@
}
};
-// InstX8632Label represents an intra-block label that is the
-// target of an intra-block branch. These are used for lowering i1
-// calculations, Select instructions, and 64-bit compares on a 32-bit
-// architecture, without basic block splitting. Basic block splitting
-// is not so desirable for several reasons, one of which is the impact
-// on decisions based on whether a variable's live range spans
-// multiple basic blocks.
+// InstX8632Label represents an intra-block label that is the target
+// of an intra-block branch. The offset between the label and the
+// branch must be fit into one byte (considered "near"). These are
+// used for lowering i1 calculations, Select instructions, and 64-bit
+// compares on a 32-bit architecture, without basic block splitting.
+// Basic block splitting is not so desirable for several reasons, one
+// of which is the impact on decisions based on whether a variable's
+// live range spans multiple basic blocks.
//
// Intra-block control flow must be used with caution. Consider the
// sequence for "c = (a >= b ? x : y)".
@@ -321,15 +322,15 @@
}
uint32_t getEmitInstCount() const override { return 0; }
IceString getName(const Cfg *Func) const;
+ SizeT getNumber() const { return Number; }
void emit(const Cfg *Func) const override;
- // TODO(jvoung): Filler in.
- void emitIAS(const Cfg *Func) const override { emit(Func); }
+ void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
private:
InstX8632Label(Cfg *Func, TargetX8632 *Target);
~InstX8632Label() override {}
- SizeT Number; // used only for unique label string generation
+ SizeT Number; // used for unique label generation.
};
// Conditional and unconditional branch instruction.
@@ -385,8 +386,7 @@
return Sum;
}
void emit(const Cfg *Func) const override;
- // TODO(jvoung): Filler in.
- void emitIAS(const Cfg *Func) const override { emit(Func); }
+ void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return isClassof(Inst, Br); }
diff --git a/src/assembler.h b/src/assembler.h
index d769b3a..d3cd9bf 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -207,7 +207,7 @@
public:
Assembler() {}
- ~Assembler() {}
+ virtual ~Assembler() {}
// Allocate a chunk of bytes using the per-Assembler allocator.
uintptr_t AllocateBytes(size_t bytes) {
@@ -224,6 +224,8 @@
// Allocate data of type T using the per-Assembler allocator.
template <typename T> T *Allocate() { return Allocator.Allocate<T>(); }
+ virtual void BindCfgNodeLabel(SizeT NodeNumber) = 0;
+
private:
llvm::BumpPtrAllocator Allocator;
};
diff --git a/src/assembler_ia32.cpp b/src/assembler_ia32.cpp
index 2b80820..11ff859 100644
--- a/src/assembler_ia32.cpp
+++ b/src/assembler_ia32.cpp
@@ -67,6 +67,53 @@
return x86::Address::Absolute(Fixup);
}
+AssemblerX86::~AssemblerX86() {
+#ifndef NDEBUG
+ for (const Label *Label : CfgNodeLabels) {
+ Label->FinalCheck();
+ }
+ for (const Label *Label : LocalLabels) {
+ Label->FinalCheck();
+ }
+#endif
+}
+
+Label *AssemblerX86::GetOrCreateLabel(SizeT Number, LabelVector &Labels) {
+ Label *L = nullptr;
+ if (Number == Labels.size()) {
+ L = new (this->Allocate<Label>()) Label();
+ Labels.push_back(L);
+ return L;
+ }
+ if (Number > Labels.size()) {
+ Labels.resize(Number + 1);
+ }
+ L = Labels[Number];
+ if (!L) {
+ L = new (this->Allocate<Label>()) Label();
+ Labels[Number] = L;
+ }
+ return L;
+}
+
+Label *AssemblerX86::GetOrCreateCfgNodeLabel(SizeT NodeNumber) {
+ return GetOrCreateLabel(NodeNumber, CfgNodeLabels);
+}
+
+Label *AssemblerX86::GetOrCreateLocalLabel(SizeT Number) {
+ return GetOrCreateLabel(Number, LocalLabels);
+}
+
+void AssemblerX86::BindCfgNodeLabel(SizeT NodeNumber) {
+ Label *L = GetOrCreateCfgNodeLabel(NodeNumber);
+ this->Bind(L);
+}
+
+void AssemblerX86::BindLocalLabel(SizeT Number) {
+ Label *L = GetOrCreateLocalLabel(Number);
+ this->Bind(L);
+}
+
void AssemblerX86::call(GPRRegister reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xFF);
diff --git a/src/assembler_ia32.h b/src/assembler_ia32.h
index bb3f0ab..b1079d1 100644
--- a/src/assembler_ia32.h
+++ b/src/assembler_ia32.h
@@ -284,7 +284,9 @@
#endif // !NDEBUG
}
- ~Label() {
+ ~Label() {}
+
+ void FinalCheck() const {
// Assert if label is being destroyed with unresolved branches pending.
assert(!IsLinked());
assert(!HasNear());
@@ -363,11 +365,16 @@
assert(!use_far_branches);
(void)use_far_branches;
}
- ~AssemblerX86() {}
+ ~AssemblerX86() override;
static const bool kNearJump = true;
static const bool kFarJump = false;
+ Label *GetOrCreateCfgNodeLabel(SizeT NodeNumber);
+ void BindCfgNodeLabel(SizeT NodeNumber) override;
+ Label *GetOrCreateLocalLabel(SizeT Number);
+ void BindLocalLabel(SizeT Number);
+
// Operations to emit GPR instructions (and dispatch on operand type).
typedef void (AssemblerX86::*TypedEmitGPR)(Type, GPRRegister);
typedef void (AssemblerX86::*TypedEmitAddr)(Type, const Address &);
@@ -848,6 +855,14 @@
void EmitGenericShift(int rm, Type Ty, const Operand &operand,
GPRRegister shifter);
+ typedef std::vector<Label *> LabelVector;
+ // A vector of pool-allocated x86 labels for CFG nodes.
+ LabelVector CfgNodeLabels;
+ // A vector of pool-allocated x86 labels for Local labels.
+ LabelVector LocalLabels;
+
+ Label *GetOrCreateLabel(SizeT Number, LabelVector &Labels);
+
AssemblerBuffer buffer_;
};