Commit 16991847 by John Porto

Subzero. Adds ldrex, strex, and dmb support (ARM32)

These instructions are used to load/store data atomically, and to notify the processor about a data memory barrier. They are used for implementing the llvm.nacl.atomic.* lowerings. BUG= https://code.google.com/p/nativeclient/issues/detail?id=4076 R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1378303003 .
parent 166cbf4a
......@@ -360,6 +360,13 @@ InstARM32Str::InstARM32Str(Cfg *Func, Variable *Value, OperandARM32Mem *Mem,
addSource(Mem);
}
InstARM32Strex::InstARM32Strex(Cfg *Func, Variable *Dest, Variable *Value,
OperandARM32Mem *Mem, CondARM32::Cond Predicate)
: InstARM32Pred(Func, InstARM32::Strex, 2, Dest, Predicate) {
addSource(Value);
addSource(Mem);
}
InstARM32Trap::InstARM32Trap(Cfg *Func)
: InstARM32(Func, InstARM32::Trap, 0, nullptr) {}
......@@ -417,6 +424,10 @@ InstARM32Vabs::InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src,
: InstARM32Pred(Func, InstARM32::Vabs, 1, Dest, Predicate) {
addSource(Src);
}
InstARM32Dmb::InstARM32Dmb(Cfg *Func)
: InstARM32Pred(Func, InstARM32::Dmb, 0, nullptr, CondARM32::AL) {}
// ======================== Dump routines ======================== //
// Two-addr ops
......@@ -433,6 +444,7 @@ template <> const char *InstARM32Uxt::Opcode = "uxt"; // still requires b/h
template <> const char *InstARM32Vsqrt::Opcode = "vsqrt";
// Mov-like ops
template <> const char *InstARM32Ldr::Opcode = "ldr";
template <> const char *InstARM32Ldrex::Opcode = "ldrex";
// Three-addr ops
template <> const char *InstARM32Adc::Opcode = "adc";
template <> const char *InstARM32Add::Opcode = "add";
......@@ -501,6 +513,7 @@ void InstARM32Mov::emitSingleDestMultiSource(const Cfg *Func) const {
assert(SrcHi->hasReg());
assert(SrcLo->hasReg());
assert(Dest->hasReg());
assert(getSrcSize() == 2);
Str << "\t"
<< "vmov" << getPredicate() << "\t";
......@@ -759,6 +772,28 @@ template <> void InstARM32Ldr::emitIAS(const Cfg *Func) const {
llvm_unreachable("Not yet implemented");
}
template <> void InstARM32Ldrex::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
assert(getSrcSize() == 1);
assert(getDest()->hasReg());
Variable *Dest = getDest();
Type DestTy = Dest->getType();
assert(isScalarIntegerType(DestTy));
const char *WidthString = getWidthString(DestTy);
Str << "\t" << Opcode << WidthString << getPredicate() << "\t";
getDest()->emit(Func);
Str << ", ";
getSrc(0)->emit(Func);
}
template <> void InstARM32Ldrex::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 1);
(void)Func;
llvm_unreachable("Not yet implemented");
}
template <> void InstARM32Movw::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
......@@ -982,6 +1017,43 @@ void InstARM32Str::dump(const Cfg *Func) const {
getSrc(0)->dump(Func);
}
void InstARM32Strex::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
assert(getSrcSize() == 2);
Type Ty = getSrc(0)->getType();
assert(isScalarIntegerType(Ty));
Variable *Dest = getDest();
Ostream &Str = Func->getContext()->getStrEmit();
static constexpr char Opcode[] = "strex";
const char *WidthString = getWidthString(Ty);
Str << "\t" << Opcode << WidthString << getPredicate() << "\t";
Dest->emit(Func);
Str << ", ";
emitSources(Func);
}
void InstARM32Strex::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 2);
(void)Func;
llvm_unreachable("Not yet implemented");
}
void InstARM32Strex::dump(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrDump();
Variable *Dest = getDest();
Dest->dump(Func);
Str << " = ";
Type Ty = getSrc(0)->getType();
dumpOpcodePred(Str, "strex", Ty);
Str << " ";
getSrc(1)->dump(Func);
Str << ", ";
getSrc(0)->dump(Func);
}
void InstARM32Trap::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
......@@ -1180,6 +1252,29 @@ void InstARM32Vabs::dump(const Cfg *Func) const {
Str << " = vabs" << getPredicate() << getVecWidthString(getSrc(0)->getType());
}
void InstARM32Dmb::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Ostream &Str = Func->getContext()->getStrEmit();
assert(getSrcSize() == 0);
Str << "\t"
"dmb"
"\t"
"sy";
}
void InstARM32Dmb::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 1);
(void)Func;
llvm_unreachable("Not yet implemented");
}
void InstARM32Dmb::dump(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
Func->getContext()->getStrDump() << "dmb\tsy";
}
void OperandARM32Mem::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
......
......@@ -291,9 +291,11 @@ public:
Call,
Cmp,
Clz,
Dmb,
Eor,
Label,
Ldr,
Ldrex,
Lsl,
Lsr,
Mla,
......@@ -313,6 +315,7 @@ public:
Sbc,
Sdiv,
Str,
Strex,
Sub,
Sxt,
Trap,
......@@ -525,24 +528,19 @@ private:
static const char *Opcode;
};
/// Base class for assignment instructions. These can be tested for redundancy
/// (and elided if redundant).
/// Base class for load instructions.
template <InstARM32::InstKindARM32 K>
class InstARM32Movlike : public InstARM32Pred {
InstARM32Movlike() = delete;
InstARM32Movlike(const InstARM32Movlike &) = delete;
InstARM32Movlike &operator=(const InstARM32Movlike &) = delete;
class InstARM32LoadBase : public InstARM32Pred {
InstARM32LoadBase() = delete;
InstARM32LoadBase(const InstARM32LoadBase &) = delete;
InstARM32LoadBase &operator=(const InstARM32LoadBase &) = delete;
public:
static InstARM32Movlike *create(Cfg *Func, Variable *Dest, Operand *Source,
CondARM32::Cond Predicate) {
return new (Func->allocate<InstARM32Movlike>())
InstARM32Movlike(Func, Dest, Source, Predicate);
}
bool isRedundantAssign() const override {
return checkForRedundantAssign(getDest(), getSrc(0));
static InstARM32LoadBase *create(Cfg *Func, Variable *Dest, Operand *Source,
CondARM32::Cond Predicate) {
return new (Func->allocate<InstARM32LoadBase>())
InstARM32LoadBase(Func, Dest, Source, Predicate);
}
bool isSimpleAssign() const override { return true; }
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override {
......@@ -558,8 +556,8 @@ public:
static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
private:
InstARM32Movlike(Cfg *Func, Variable *Dest, Operand *Source,
CondARM32::Cond Predicate)
InstARM32LoadBase(Cfg *Func, Variable *Dest, Operand *Source,
CondARM32::Cond Predicate)
: InstARM32Pred(Func, K, 1, Dest, Predicate) {
addSource(Source);
}
......@@ -778,7 +776,8 @@ using InstARM32Vadd = InstARM32ThreeAddrFP<InstARM32::Vadd>;
using InstARM32Vdiv = InstARM32ThreeAddrFP<InstARM32::Vdiv>;
using InstARM32Vmul = InstARM32ThreeAddrFP<InstARM32::Vmul>;
using InstARM32Vsub = InstARM32ThreeAddrFP<InstARM32::Vsub>;
using InstARM32Ldr = InstARM32Movlike<InstARM32::Ldr>;
using InstARM32Ldr = InstARM32LoadBase<InstARM32::Ldr>;
using InstARM32Ldrex = InstARM32LoadBase<InstARM32::Ldrex>;
/// MovT leaves the bottom bits alone so dest is also a source. This helps
/// indicate that a previous MovW setting dest is not dead code.
using InstARM32Movt = InstARM32TwoAddrGPR<InstARM32::Movt>;
......@@ -1020,7 +1019,7 @@ private:
/// Store instruction. It's important for liveness that there is no Dest operand
/// (OperandARM32Mem instead of Dest Variable).
class InstARM32Str : public InstARM32Pred {
class InstARM32Str final : public InstARM32Pred {
InstARM32Str() = delete;
InstARM32Str(const InstARM32Str &) = delete;
InstARM32Str &operator=(const InstARM32Str &) = delete;
......@@ -1042,6 +1041,32 @@ private:
CondARM32::Cond Predicate);
};
/// Exclusive Store instruction. Like its non-exclusive sibling, it's important
/// for liveness that there is no Dest operand (OperandARM32Mem instead of Dest
/// Variable).
class InstARM32Strex final : public InstARM32Pred {
InstARM32Strex() = delete;
InstARM32Strex(const InstARM32Strex &) = delete;
InstARM32Strex &operator=(const InstARM32Strex &) = delete;
public:
/// Value must be a register.
static InstARM32Strex *create(Cfg *Func, Variable *Dest, Variable *Value,
OperandARM32Mem *Mem,
CondARM32::Cond Predicate) {
return new (Func->allocate<InstARM32Strex>())
InstARM32Strex(Func, Dest, Value, Mem, Predicate);
}
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return isClassof(Inst, Strex); }
private:
InstARM32Strex(Cfg *Func, Variable *Dest, Variable *Value,
OperandARM32Mem *Mem, CondARM32::Cond Predicate);
};
class InstARM32Trap : public InstARM32 {
InstARM32Trap() = delete;
InstARM32Trap(const InstARM32Trap &) = delete;
......@@ -1213,6 +1238,25 @@ private:
InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src,
CondARM32::Cond Predicate);
};
class InstARM32Dmb final : public InstARM32Pred {
InstARM32Dmb() = delete;
InstARM32Dmb(const InstARM32Dmb &) = delete;
InstARM32Dmb &operator=(const InstARM32Dmb &) = delete;
public:
static InstARM32Dmb *create(Cfg *Func) {
return new (Func->allocate<InstARM32Dmb>()) InstARM32Dmb(Func);
}
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return isClassof(Inst, Dmb); }
private:
explicit InstARM32Dmb(Cfg *Func);
};
// Declare partial template specializations of emit() methods that already have
// default implementations. Without this, there is the possibility of ODR
// violations and link errors.
......
......@@ -241,6 +241,7 @@ protected:
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Clz::create(Func, Dest, Src0, Pred));
}
void _dmb() { Context.insert(InstARM32Dmb::create(Func)); }
void _eor(Variable *Dest, Variable *Src0, Operand *Src1,
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Eor::create(Func, Dest, Src0, Src1, Pred));
......@@ -253,6 +254,14 @@ protected:
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Ldr::create(Func, Dest, Addr, Pred));
}
void _ldrex(Variable *Dest, OperandARM32Mem *Addr,
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Ldrex::create(Func, Dest, Addr, Pred));
if (auto *Dest64 = llvm::dyn_cast<Variable64On32>(Dest)) {
Context.insert(InstFakeDef::create(Func, Dest64->getLo(), Dest));
Context.insert(InstFakeDef::create(Func, Dest64->getHi(), Dest));
}
}
void _lsl(Variable *Dest, Variable *Src0, Operand *Src1,
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Lsl::create(Func, Dest, Src0, Src1, Pred));
......@@ -374,6 +383,14 @@ protected:
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Str::create(Func, Value, Addr, Pred));
}
void _strex(Variable *Dest, Variable *Value, OperandARM32Mem *Addr,
CondARM32::Cond Pred = CondARM32::AL) {
if (auto *Value64 = llvm::dyn_cast<Variable64On32>(Value)) {
Context.insert(InstFakeUse::create(Func, Value64->getLo()));
Context.insert(InstFakeUse::create(Func, Value64->getHi()));
}
Context.insert(InstARM32Strex::create(Func, Dest, Value, Addr, Pred));
}
void _sub(Variable *Dest, Variable *Src0, Operand *Src1,
CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Sub::create(Func, Dest, Src0, Src1, Pred));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment