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, ...@@ -360,6 +360,13 @@ InstARM32Str::InstARM32Str(Cfg *Func, Variable *Value, OperandARM32Mem *Mem,
addSource(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) InstARM32Trap::InstARM32Trap(Cfg *Func)
: InstARM32(Func, InstARM32::Trap, 0, nullptr) {} : InstARM32(Func, InstARM32::Trap, 0, nullptr) {}
...@@ -417,6 +424,10 @@ InstARM32Vabs::InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src, ...@@ -417,6 +424,10 @@ InstARM32Vabs::InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src,
: InstARM32Pred(Func, InstARM32::Vabs, 1, Dest, Predicate) { : InstARM32Pred(Func, InstARM32::Vabs, 1, Dest, Predicate) {
addSource(Src); addSource(Src);
} }
InstARM32Dmb::InstARM32Dmb(Cfg *Func)
: InstARM32Pred(Func, InstARM32::Dmb, 0, nullptr, CondARM32::AL) {}
// ======================== Dump routines ======================== // // ======================== Dump routines ======================== //
// Two-addr ops // Two-addr ops
...@@ -433,6 +444,7 @@ template <> const char *InstARM32Uxt::Opcode = "uxt"; // still requires b/h ...@@ -433,6 +444,7 @@ template <> const char *InstARM32Uxt::Opcode = "uxt"; // still requires b/h
template <> const char *InstARM32Vsqrt::Opcode = "vsqrt"; template <> const char *InstARM32Vsqrt::Opcode = "vsqrt";
// Mov-like ops // Mov-like ops
template <> const char *InstARM32Ldr::Opcode = "ldr"; template <> const char *InstARM32Ldr::Opcode = "ldr";
template <> const char *InstARM32Ldrex::Opcode = "ldrex";
// Three-addr ops // Three-addr ops
template <> const char *InstARM32Adc::Opcode = "adc"; template <> const char *InstARM32Adc::Opcode = "adc";
template <> const char *InstARM32Add::Opcode = "add"; template <> const char *InstARM32Add::Opcode = "add";
...@@ -501,6 +513,7 @@ void InstARM32Mov::emitSingleDestMultiSource(const Cfg *Func) const { ...@@ -501,6 +513,7 @@ void InstARM32Mov::emitSingleDestMultiSource(const Cfg *Func) const {
assert(SrcHi->hasReg()); assert(SrcHi->hasReg());
assert(SrcLo->hasReg()); assert(SrcLo->hasReg());
assert(Dest->hasReg()); assert(Dest->hasReg());
assert(getSrcSize() == 2);
Str << "\t" Str << "\t"
<< "vmov" << getPredicate() << "\t"; << "vmov" << getPredicate() << "\t";
...@@ -759,6 +772,28 @@ template <> void InstARM32Ldr::emitIAS(const Cfg *Func) const { ...@@ -759,6 +772,28 @@ template <> void InstARM32Ldr::emitIAS(const Cfg *Func) const {
llvm_unreachable("Not yet implemented"); 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 { template <> void InstARM32Movw::emit(const Cfg *Func) const {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
return; return;
...@@ -982,6 +1017,43 @@ void InstARM32Str::dump(const Cfg *Func) const { ...@@ -982,6 +1017,43 @@ void InstARM32Str::dump(const Cfg *Func) const {
getSrc(0)->dump(Func); 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 { void InstARM32Trap::emit(const Cfg *Func) const {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
return; return;
...@@ -1180,6 +1252,29 @@ void InstARM32Vabs::dump(const Cfg *Func) const { ...@@ -1180,6 +1252,29 @@ void InstARM32Vabs::dump(const Cfg *Func) const {
Str << " = vabs" << getPredicate() << getVecWidthString(getSrc(0)->getType()); 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 { void OperandARM32Mem::emit(const Cfg *Func) const {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
return; return;
......
...@@ -291,9 +291,11 @@ public: ...@@ -291,9 +291,11 @@ public:
Call, Call,
Cmp, Cmp,
Clz, Clz,
Dmb,
Eor, Eor,
Label, Label,
Ldr, Ldr,
Ldrex,
Lsl, Lsl,
Lsr, Lsr,
Mla, Mla,
...@@ -313,6 +315,7 @@ public: ...@@ -313,6 +315,7 @@ public:
Sbc, Sbc,
Sdiv, Sdiv,
Str, Str,
Strex,
Sub, Sub,
Sxt, Sxt,
Trap, Trap,
...@@ -525,24 +528,19 @@ private: ...@@ -525,24 +528,19 @@ private:
static const char *Opcode; static const char *Opcode;
}; };
/// Base class for assignment instructions. These can be tested for redundancy /// Base class for load instructions.
/// (and elided if redundant).
template <InstARM32::InstKindARM32 K> template <InstARM32::InstKindARM32 K>
class InstARM32Movlike : public InstARM32Pred { class InstARM32LoadBase : public InstARM32Pred {
InstARM32Movlike() = delete; InstARM32LoadBase() = delete;
InstARM32Movlike(const InstARM32Movlike &) = delete; InstARM32LoadBase(const InstARM32LoadBase &) = delete;
InstARM32Movlike &operator=(const InstARM32Movlike &) = delete; InstARM32LoadBase &operator=(const InstARM32LoadBase &) = delete;
public: public:
static InstARM32Movlike *create(Cfg *Func, Variable *Dest, Operand *Source, static InstARM32LoadBase *create(Cfg *Func, Variable *Dest, Operand *Source,
CondARM32::Cond Predicate) { CondARM32::Cond Predicate) {
return new (Func->allocate<InstARM32Movlike>()) return new (Func->allocate<InstARM32LoadBase>())
InstARM32Movlike(Func, Dest, Source, Predicate); InstARM32LoadBase(Func, Dest, Source, Predicate);
}
bool isRedundantAssign() const override {
return checkForRedundantAssign(getDest(), getSrc(0));
} }
bool isSimpleAssign() const override { return true; }
void emit(const Cfg *Func) const override; void emit(const Cfg *Func) const override;
void emitIAS(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override { void dump(const Cfg *Func) const override {
...@@ -558,8 +556,8 @@ public: ...@@ -558,8 +556,8 @@ public:
static bool classof(const Inst *Inst) { return isClassof(Inst, K); } static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
private: private:
InstARM32Movlike(Cfg *Func, Variable *Dest, Operand *Source, InstARM32LoadBase(Cfg *Func, Variable *Dest, Operand *Source,
CondARM32::Cond Predicate) CondARM32::Cond Predicate)
: InstARM32Pred(Func, K, 1, Dest, Predicate) { : InstARM32Pred(Func, K, 1, Dest, Predicate) {
addSource(Source); addSource(Source);
} }
...@@ -778,7 +776,8 @@ using InstARM32Vadd = InstARM32ThreeAddrFP<InstARM32::Vadd>; ...@@ -778,7 +776,8 @@ using InstARM32Vadd = InstARM32ThreeAddrFP<InstARM32::Vadd>;
using InstARM32Vdiv = InstARM32ThreeAddrFP<InstARM32::Vdiv>; using InstARM32Vdiv = InstARM32ThreeAddrFP<InstARM32::Vdiv>;
using InstARM32Vmul = InstARM32ThreeAddrFP<InstARM32::Vmul>; using InstARM32Vmul = InstARM32ThreeAddrFP<InstARM32::Vmul>;
using InstARM32Vsub = InstARM32ThreeAddrFP<InstARM32::Vsub>; 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 /// 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. /// indicate that a previous MovW setting dest is not dead code.
using InstARM32Movt = InstARM32TwoAddrGPR<InstARM32::Movt>; using InstARM32Movt = InstARM32TwoAddrGPR<InstARM32::Movt>;
...@@ -1020,7 +1019,7 @@ private: ...@@ -1020,7 +1019,7 @@ private:
/// Store instruction. It's important for liveness that there is no Dest operand /// Store instruction. It's important for liveness that there is no Dest operand
/// (OperandARM32Mem instead of Dest Variable). /// (OperandARM32Mem instead of Dest Variable).
class InstARM32Str : public InstARM32Pred { class InstARM32Str final : public InstARM32Pred {
InstARM32Str() = delete; InstARM32Str() = delete;
InstARM32Str(const InstARM32Str &) = delete; InstARM32Str(const InstARM32Str &) = delete;
InstARM32Str &operator=(const InstARM32Str &) = delete; InstARM32Str &operator=(const InstARM32Str &) = delete;
...@@ -1042,6 +1041,32 @@ private: ...@@ -1042,6 +1041,32 @@ private:
CondARM32::Cond Predicate); 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 { class InstARM32Trap : public InstARM32 {
InstARM32Trap() = delete; InstARM32Trap() = delete;
InstARM32Trap(const InstARM32Trap &) = delete; InstARM32Trap(const InstARM32Trap &) = delete;
...@@ -1213,6 +1238,25 @@ private: ...@@ -1213,6 +1238,25 @@ private:
InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src, InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src,
CondARM32::Cond Predicate); 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 // Declare partial template specializations of emit() methods that already have
// default implementations. Without this, there is the possibility of ODR // default implementations. Without this, there is the possibility of ODR
// violations and link errors. // violations and link errors.
......
...@@ -241,6 +241,7 @@ protected: ...@@ -241,6 +241,7 @@ protected:
CondARM32::Cond Pred = CondARM32::AL) { CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Clz::create(Func, Dest, Src0, Pred)); Context.insert(InstARM32Clz::create(Func, Dest, Src0, Pred));
} }
void _dmb() { Context.insert(InstARM32Dmb::create(Func)); }
void _eor(Variable *Dest, Variable *Src0, Operand *Src1, void _eor(Variable *Dest, Variable *Src0, Operand *Src1,
CondARM32::Cond Pred = CondARM32::AL) { CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Eor::create(Func, Dest, Src0, Src1, Pred)); Context.insert(InstARM32Eor::create(Func, Dest, Src0, Src1, Pred));
...@@ -253,6 +254,14 @@ protected: ...@@ -253,6 +254,14 @@ protected:
CondARM32::Cond Pred = CondARM32::AL) { CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Ldr::create(Func, Dest, Addr, Pred)); 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, void _lsl(Variable *Dest, Variable *Src0, Operand *Src1,
CondARM32::Cond Pred = CondARM32::AL) { CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Lsl::create(Func, Dest, Src0, Src1, Pred)); Context.insert(InstARM32Lsl::create(Func, Dest, Src0, Src1, Pred));
...@@ -374,6 +383,14 @@ protected: ...@@ -374,6 +383,14 @@ protected:
CondARM32::Cond Pred = CondARM32::AL) { CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Str::create(Func, Value, Addr, Pred)); 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, void _sub(Variable *Dest, Variable *Src0, Operand *Src1,
CondARM32::Cond Pred = CondARM32::AL) { CondARM32::Cond Pred = CondARM32::AL) {
Context.insert(InstARM32Sub::create(Func, Dest, Src0, Src1, Pred)); 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