Subzero. Implements the scalar bitcast operations for ARM32.
BUG= https://code.google.com/p/nativeclient/issues/detail?id=4076
R=stichnot@chromium.org
Review URL: https://codereview.chromium.org/1343783003 .
diff --git a/src/IceInstARM32.cpp b/src/IceInstARM32.cpp
index 375132b..9a68115 100644
--- a/src/IceInstARM32.cpp
+++ b/src/IceInstARM32.cpp
@@ -403,7 +403,6 @@
template <> const char *InstARM32Mov::Opcode = "mov";
// FP
template <> const char *InstARM32Vldr::Opcode = "vldr";
-template <> const char *InstARM32Vmov::Opcode = "vmov";
// Three-addr ops
template <> const char *InstARM32Adc::Opcode = "adc";
template <> const char *InstARM32Add::Opcode = "add";
@@ -498,15 +497,56 @@
llvm_unreachable("Not yet implemented");
}
-template <> void InstARM32Vmov::emit(const Cfg *Func) const {
+void InstARM32Vmov::emitMultiDestSingleSource(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
- assert(CondARM32::AL == getPredicate());
Ostream &Str = Func->getContext()->getStrEmit();
- assert(getSrcSize() == 1);
+ Variable *Dest0 = getDest();
+ Operand *Src0 = getSrc(0);
+
+ assert(Dest0->hasReg());
+ assert(Dest1->hasReg());
+ assert(!llvm::isa<OperandARM32Mem>(Src0));
+
+ Str << "\t"
+ << "vmov"
+ << "\t";
+ Dest0->emit(Func);
+ Str << ", ";
+ Dest1->emit(Func);
+ Str << ", ";
+ Src0->emit(Func);
+}
+
+void InstARM32Vmov::emitSingleDestMultiSource(const Cfg *Func) const {
+ if (!BuildDefs::dump())
+ return;
+ Ostream &Str = Func->getContext()->getStrEmit();
+ Variable *Dest0 = getDest();
+ Operand *Src0 = getSrc(0);
+ Operand *Src1 = getSrc(1);
+
+ assert(Dest0->hasReg());
+ assert(!llvm::isa<OperandARM32Mem>(Src0));
+ assert(!llvm::isa<OperandARM32Mem>(Src1));
+
+ Str << "\t"
+ << "vmov"
+ << "\t";
+ Dest0->emit(Func);
+ Str << ", ";
+ Src0->emit(Func);
+ Str << ", ";
+ Src1->emit(Func);
+}
+
+void InstARM32Vmov::emitSingleDestSingleSource(const Cfg *Func) const {
+ if (!BuildDefs::dump())
+ return;
+ Ostream &Str = Func->getContext()->getStrEmit();
Variable *Dest = getDest();
if (Dest->hasReg()) {
- IceString ActualOpcode = Opcode;
+ IceString ActualOpcode = "vmov";
Operand *Src0 = getSrc(0);
if (const auto *Src0V = llvm::dyn_cast<Variable>(Src0)) {
if (!Src0V->hasReg()) {
@@ -532,12 +572,41 @@
}
}
-template <> void InstARM32Vmov::emitIAS(const Cfg *Func) const {
+void InstARM32Vmov::emit(const Cfg *Func) const {
+ if (!BuildDefs::dump())
+ return;
+ assert(CondARM32::AL == getPredicate());
+ assert(isMultiDest() + isMultiSource() <= 1 && "Invalid vmov type.");
+ if (isMultiDest()) {
+ emitMultiDestSingleSource(Func);
+ return;
+ }
+
+ if (isMultiSource()) {
+ emitSingleDestMultiSource(Func);
+ return;
+ }
+
+ emitSingleDestSingleSource(Func);
+}
+
+void InstARM32Vmov::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 1);
(void)Func;
llvm_unreachable("Not yet implemented");
}
+void InstARM32Vmov::dump(const Cfg *Func) const {
+ if (!BuildDefs::dump())
+ return;
+ Ostream &Str = Func->getContext()->getStrDump();
+ dumpOpcodePred(Str, "vmov", getDest()->getType());
+ Str << " ";
+ dumpDest(Func);
+ Str << ", ";
+ dumpSources(Func);
+}
+
void InstARM32Br::emit(const Cfg *Func) const {
if (!BuildDefs::dump())
return;
diff --git a/src/IceInstARM32.h b/src/IceInstARM32.h
index 56bf8cd..8c2ea6f 100644
--- a/src/IceInstARM32.h
+++ b/src/IceInstARM32.h
@@ -785,7 +785,6 @@
using InstARM32Mov = InstARM32Movlike<InstARM32::Mov>;
/// Represents various vector mov instruction forms (simple single source,
/// single dest forms only, not the 2 GPR <-> 1 D reg forms, etc.).
-using InstARM32Vmov = InstARM32Movlike<InstARM32::Vmov>;
using InstARM32Vldr = InstARM32Movlike<InstARM32::Vldr>;
/// 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.
@@ -1119,6 +1118,93 @@
const VcvtVariant Variant;
};
+/// Handles (some of) vmov's various formats.
+class InstARM32Vmov final : public InstARM32Pred {
+ InstARM32Vmov() = delete;
+ InstARM32Vmov(const InstARM32Vmov &) = delete;
+ InstARM32Vmov &operator=(const InstARM32Vmov &) = delete;
+
+public:
+ /// RegisterPair is used to group registers in
+ ///
+ /// vmov D, (R, R)
+ ///
+ /// and
+ ///
+ /// vmov (R, R), D
+ struct RegisterPair {
+ explicit RegisterPair(Variable *V0, Variable *V1) : _0(V0), _1(V1) {
+ assert(V0->getType() == IceType_i32);
+ assert(V1->getType() == IceType_i32);
+ }
+ Variable *_0;
+ Variable *_1;
+ };
+
+ static InstARM32Vmov *create(Cfg *Func, Variable *Dest, Operand *Src,
+ CondARM32::Cond Predicate) {
+ return new (Func->allocate<InstARM32Vmov>())
+ InstARM32Vmov(Func, Dest, Src, Predicate);
+ }
+ static InstARM32Vmov *create(Cfg *Func, const RegisterPair &Dests,
+ Variable *Src, CondARM32::Cond Predicate) {
+ return new (Func->allocate<InstARM32Vmov>())
+ InstARM32Vmov(Func, Dests, Src, Predicate);
+ }
+ static InstARM32Vmov *create(Cfg *Func, Variable *Dest,
+ const RegisterPair &Srcs,
+ CondARM32::Cond Predicate) {
+ return new (Func->allocate<InstARM32Vmov>())
+ InstARM32Vmov(Func, Dest, Srcs, Predicate);
+ }
+ bool isRedundantAssign() const override {
+ return Dest1 == nullptr && getSrcSize() == 1 &&
+ checkForRedundantAssign(getDest(), getSrc(0));
+ }
+ 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;
+ static bool classof(const Inst *Inst) { return isClassof(Inst, Vmov); }
+
+private:
+ InstARM32Vmov(Cfg *Func, Variable *Dest, Operand *Src,
+ CondARM32::Cond Predicate)
+ : InstARM32Pred(Func, InstARM32::Vmov, 1, Dest, Predicate) {
+ addSource(Src);
+ }
+
+ InstARM32Vmov(Cfg *Func, const RegisterPair &Dests, Variable *Src,
+ CondARM32::Cond Predicate)
+ : InstARM32Pred(Func, InstARM32::Vmov, 1, Dests._0, Predicate),
+ Dest1(Dests._1) {
+ addSource(Src);
+ }
+
+ InstARM32Vmov(Cfg *Func, Variable *Dest, const RegisterPair &Srcs,
+ CondARM32::Cond Predicate)
+ : InstARM32Pred(Func, InstARM32::Vmov, 2, Dest, Predicate) {
+ addSource(Srcs._0);
+ addSource(Srcs._1);
+ }
+
+ bool isMultiDest() const {
+ assert(getDest() != nullptr);
+ return Dest1 != nullptr;
+ }
+
+ bool isMultiSource() const {
+ assert(getSrcSize() >= 1);
+ return getSrcSize() > 1;
+ }
+
+ void emitMultiDestSingleSource(const Cfg *Func) const;
+ void emitSingleDestMultiSource(const Cfg *Func) const;
+ void emitSingleDestSingleSource(const Cfg *Func) const;
+
+ Variable *Dest1 = nullptr;
+};
+
// Declare partial template specializations of emit() methods that
// already have default implementations. Without this, there is the
// possibility of ODR violations and link errors.
@@ -1128,7 +1214,6 @@
template <> void InstARM32Movw::emit(const Cfg *Func) const;
template <> void InstARM32Movt::emit(const Cfg *Func) const;
template <> void InstARM32Vldr::emit(const Cfg *Func) const;
-template <> void InstARM32Vmov::emit(const Cfg *Func) const;
} // end of namespace Ice
diff --git a/src/IceTargetLoweringARM32.cpp b/src/IceTargetLoweringARM32.cpp
index 3e92637..0634e45 100644
--- a/src/IceTargetLoweringARM32.cpp
+++ b/src/IceTargetLoweringARM32.cpp
@@ -2108,6 +2108,9 @@
UnimplementedError(Func->getContext()->getFlags());
break;
} else if (Dest->getType() == IceType_i64) {
+ split64(Dest);
+ Context.insert(InstFakeDef::create(Func, Dest->getLo()));
+ Context.insert(InstFakeDef::create(Func, Dest->getHi()));
UnimplementedError(Func->getContext()->getFlags());
break;
}
@@ -2144,7 +2147,10 @@
if (isVectorType(Dest->getType())) {
UnimplementedError(Func->getContext()->getFlags());
break;
- } else if (Src0->getType() == IceType_i64) {
+ }
+ if (Src0->getType() == IceType_i64) {
+ // avoid cryptic liveness errors
+ Context.insert(InstFakeDef::create(Func, Dest));
UnimplementedError(Func->getContext()->getFlags());
break;
}
@@ -2178,7 +2184,77 @@
lowerAssign(Assign);
return;
}
- UnimplementedError(Func->getContext()->getFlags());
+ Type DestType = Dest->getType();
+ switch (DestType) {
+ case IceType_NUM:
+ case IceType_void:
+ llvm::report_fatal_error("Unexpected bitcast.");
+ case IceType_i1:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_v4i1:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_i8:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_i16:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_i32:
+ case IceType_f32: {
+ Variable *Src0R = legalizeToReg(Src0);
+ Variable *T = makeReg(DestType);
+ _vmov(T, Src0R);
+ lowerAssign(InstAssign::create(Func, Dest, T));
+ break;
+ }
+ case IceType_i64: {
+ // t0, t1 <- src0
+ // dest[31..0] = t0
+ // dest[63..32] = t1
+ assert(Src0->getType() == IceType_f64);
+ Variable *T0 = makeReg(IceType_i32);
+ Variable *T1 = makeReg(IceType_i32);
+ Variable *Src0R = legalizeToReg(Src0);
+ split64(Dest);
+ _vmov(InstARM32Vmov::RegisterPair(T0, T1), Src0R);
+ lowerAssign(InstAssign::create(Func, Dest->getLo(), T0));
+ lowerAssign(InstAssign::create(Func, Dest->getHi(), T1));
+ break;
+ }
+ case IceType_f64: {
+ // T0 <- lo(src)
+ // T1 <- hi(src)
+ // vmov T2, T0, T1
+ // Dest <- T2
+ assert(Src0->getType() == IceType_i64);
+ Variable *SrcLo = legalizeToReg(loOperand(Src0));
+ Variable *SrcHi = legalizeToReg(hiOperand(Src0));
+ Variable *T = makeReg(IceType_f64);
+ _vmov(T, InstARM32Vmov::RegisterPair(SrcLo, SrcHi));
+ lowerAssign(InstAssign::create(Func, Dest, T));
+ break;
+ }
+ case IceType_v8i1:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_v16i1:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_v8i16:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_v16i8:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_v4i32:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ case IceType_v4f32:
+ UnimplementedError(Func->getContext()->getFlags());
+ break;
+ }
break;
}
}
diff --git a/src/IceTargetLoweringARM32.h b/src/IceTargetLoweringARM32.h
index 1370368..57e0b5a 100644
--- a/src/IceTargetLoweringARM32.h
+++ b/src/IceTargetLoweringARM32.h
@@ -415,6 +415,19 @@
constexpr CondARM32::Cond Pred = CondARM32::AL;
Context.insert(InstARM32Vmov::create(Func, Dest, Src0, Pred));
}
+ // This represents the single source, multi dest variant.
+ void _vmov(InstARM32Vmov::RegisterPair Dests, Variable *Src0) {
+ constexpr CondARM32::Cond Pred = CondARM32::AL;
+ Context.insert(InstARM32Vmov::create(Func, Dests, Src0, Pred));
+ // The Vmov instruction created above does not define Dests._1. Therefore
+ // we add a Dest._1 = FakeDef pseudo instruction.
+ Context.insert(InstFakeDef::create(Func, Dests._1));
+ }
+ // This represents the multi source, single dest variant.
+ void _vmov(Variable *Dest, InstARM32Vmov::RegisterPair Srcs) {
+ constexpr CondARM32::Cond Pred = CondARM32::AL;
+ Context.insert(InstARM32Vmov::create(Func, Dest, Srcs, Pred));
+ }
void _vmul(Variable *Dest, Variable *Src0, Variable *Src1) {
Context.insert(InstARM32Vmul::create(Func, Dest, Src0, Src1));
}
diff --git a/tests_lit/llvm2ice_tests/bitcast.ll b/tests_lit/llvm2ice_tests/bitcast.ll
index 40a654c..388afb1 100644
--- a/tests_lit/llvm2ice_tests/bitcast.ll
+++ b/tests_lit/llvm2ice_tests/bitcast.ll
@@ -3,6 +3,16 @@
; RUN: %p2i --filetype=obj --disassemble -i %s --args -O2 | FileCheck %s
; RUN: %p2i --filetype=obj --disassemble -i %s --args -Om1 | FileCheck %s
+; RUN: %if --need=allow_dump --need=target_ARM32 --command %p2i --filetype=asm \
+; RUN: --target arm32 -i %s --args -O2 --skip-unimplemented \
+; RUN: | %if --need=allow_dump --need=target_ARM32 --command FileCheck %s \
+; RUN: --check-prefix=ARM32
+
+; RUN: %if --need=allow_dump --need=target_ARM32 --command %p2i --filetype=asm \
+; RUN: --target arm32 -i %s --args -Om1 --skip-unimplemented \
+; RUN: | %if --need=allow_dump --need=target_ARM32 --command FileCheck %s \
+; RUN: --check-prefix=ARM32
+
define internal i32 @cast_f2i(float %f) {
entry:
%v0 = bitcast float %f to i32
@@ -10,6 +20,8 @@
}
; CHECK-LABEL: cast_f2i
; CHECK: mov eax
+; ARM32-LABEL: cast_f2i
+; ARM32: vmov r{{[0-9]+}}, s{{[0-9]+}}
define internal float @cast_i2f(i32 %i) {
entry:
@@ -18,6 +30,8 @@
}
; CHECK-LABEL: cast_i2f
; CHECK: fld DWORD PTR
+; ARM32-LABEL: cast_i2f
+; ARM32: vmov s{{[0-9]+}}, r{{[0-9]+}}
define internal i64 @cast_d2ll(double %d) {
entry:
@@ -26,6 +40,8 @@
}
; CHECK-LABEL: cast_d2ll
; CHECK: mov edx
+; ARM32-LABEL: cast_d2ll
+; ARM32: vmov r{{[0-9]+}}, r{{[0-9]+}}, d{{[0-9]+}}
define internal i64 @cast_d2ll_const() {
entry:
@@ -35,6 +51,11 @@
; CHECK-LABEL: cast_d2ll_const
; CHECK: mov e{{..}},DWORD PTR ds:0x0 {{.*}} .L$double$0
; CHECK: mov e{{..}},DWORD PTR ds:0x4 {{.*}} .L$double$0
+; ARM32-LABEL: cast_d2ll_const
+; ARM32-DAG: movw [[ADDR:r[0-9]+]], #:lower16:.L$
+; ARM32-DAG: movt [[ADDR]], #:upper16:.L$
+; ARM32-DAG: vldr [[DREG:d[0-9]+]], {{\[}}[[ADDR]], #0{{\]}}
+; ARM32: vmov r{{[0-9]+}}, r{{[0-9]+}}, [[DREG]]
define internal double @cast_ll2d(i64 %ll) {
entry:
@@ -43,6 +64,8 @@
}
; CHECK-LABEL: cast_ll2d
; CHECK: fld QWORD PTR
+; ARM32-LABEL: cast_ll2d
+; ARM32: vmov d{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}
define internal double @cast_ll2d_const() {
entry:
@@ -53,3 +76,8 @@
; CHECK: mov {{.*}},0x73ce2ff2
; CHECK: mov {{.*}},0xb3a
; CHECK: fld QWORD PTR
+; ARM32-LABEL: cast_ll2d_const
+; ARM32-DAG: movw [[REG0:r[0-9]+]], #12274
+; ARM32-DAG: movt [[REG0:r[0-9]+]], #29646
+; ARM32-DAG: movw [[REG1:r[0-9]+]], #2874
+; ARM32: vmov d{{[0-9]+}}, [[REG0]], [[REG1]]
diff --git a/tests_lit/llvm2ice_tests/fp.convert.ll b/tests_lit/llvm2ice_tests/fp.convert.ll
index 1efe0d7..18c0951 100644
--- a/tests_lit/llvm2ice_tests/fp.convert.ll
+++ b/tests_lit/llvm2ice_tests/fp.convert.ll
@@ -7,8 +7,14 @@
; RUN: %p2i --filetype=obj --disassemble -i %s --args -Om1 | FileCheck %s
; RUN: %if --need=allow_dump --need=target_ARM32 --command %p2i --filetype=asm \
+; RUN: --target arm32 -i %s --args -O2 --skip-unimplemented \
+; RUN: | %if --need=allow_dump --need=target_ARM32 --command FileCheck %s \
+; RUN: --check-prefix=ARM32
+
+; RUN: %if --need=allow_dump --need=target_ARM32 --command %p2i --filetype=asm \
; RUN: --target arm32 -i %s --args -Om1 --skip-unimplemented \
-; RUN: | %if --need=target_ARM32 --command FileCheck %s --check-prefix=ARM32
+; RUN: | %if --need=allow_dump --need=target_ARM32 --command FileCheck %s \
+; RUN: --check-prefix=ARM32
define internal float @fptrunc(double %a) {
entry:
@@ -554,8 +560,7 @@
; CHECK-LABEL: int32BitcastToFloat
; CHECK: mov
; ARM32-LABEL: int32BitcastToFloat
-; TODO(jpp): implement this test.
-
+; ARM32: vmov s{{[0-9]+}}, r{{[0-9]+}}
define internal float @int32BitcastToFloatConst() {
entry:
%conv = bitcast i32 8675309 to float
@@ -564,7 +569,9 @@
; CHECK-LABEL: int32BitcastToFloatConst
; CHECK: mov
; ARM32-LABEL: int32BitcastToFloatConst
-; TODO(jpp): implement this test.
+; ARM32-DAG: movw [[REG:r[0-9]+]], #24557
+; ARM32-DAG: movt [[REG]], #132
+; ARM32: vmov s{{[0-9]+}}, [[REG]]
define internal double @int64BitcastToDouble(i64 %a) {
entry:
@@ -574,7 +581,7 @@
; CHECK-LABEL: int64BitcastToDouble
; CHECK: mov
; ARM32-LABEL: int64BitcastToDouble
-; TODO(jpp): implement this test.
+; ARM32: vmov d{{[0-9]+}}, r{{[0-9]+}}, r{{[0-9]+}}
define internal double @int64BitcastToDoubleConst() {
entry:
@@ -584,5 +591,7 @@
; CHECK-LABEL: int64BitcastToDoubleConst
; CHECK: mov
; ARM32-LABEL: int64BitcastToDoubleConst
-; TODO(jpp): implement this test.
-
+; ARM32-DAG: movw [[REG0:r[0-9]+]], #57336
+; ARM32-DAG: movt [[REG0]], #137
+; ARM32-DAG: movw [[REG1:r[0-9]+]], #0
+; ARM32-DAG: vmov d{{[0-9]+}}, [[REG0]], [[REG1]]