Fix x86 floating-point constant emission.
Previously, the basis of constant pooling was implemented, but two things were lacking:
1. The constant pools were not being emitted in the asm file.
2. A direct FP value was emitted in an FP instruction, e.g. "addss xmm0, 1.0000e00". Curiously, at least for some FP constants, llvm-mc was accepting this syntax.
BUG= none
R=jfb@chromium.org
Review URL: https://codereview.chromium.org/291213003
diff --git a/src/IceDefs.h b/src/IceDefs.h
index 6c5cb1a..9870716 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -61,6 +61,7 @@
typedef std::list<InstPhi *> PhiList;
typedef std::vector<Variable *> VarList;
typedef std::vector<CfgNode *> NodeList;
+typedef std::vector<Constant *> ConstantList;
// SizeT is for holding small-ish limits like number of source
// operands in an instruction. It is used instead of size_t (which
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index ab63b4c..7a21b40 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -34,16 +34,27 @@
TypePool &operator=(const TypePool &) LLVM_DELETED_FUNCTION;
public:
- TypePool() {}
+ TypePool() : NextPoolID(0) {}
ValueType *getOrAdd(GlobalContext *Ctx, Type Ty, KeyType Key) {
TupleType TupleKey = std::make_pair(Ty, Key);
typename ContainerType::const_iterator Iter = Pool.find(TupleKey);
if (Iter != Pool.end())
return Iter->second;
- ValueType *Result = ValueType::create(Ctx, Ty, Key);
+ ValueType *Result = ValueType::create(Ctx, Ty, Key, NextPoolID++);
Pool[TupleKey] = Result;
return Result;
}
+ ConstantList getConstantPool() const {
+ ConstantList Constants;
+ Constants.reserve(Pool.size());
+ // TODO: replace the loop with std::transform + lambdas.
+ for (typename ContainerType::const_iterator I = Pool.begin(),
+ E = Pool.end();
+ I != E; ++I) {
+ Constants.push_back(I->second);
+ }
+ return Constants;
+ }
private:
typedef std::pair<Type, KeyType> TupleType;
@@ -58,6 +69,7 @@
};
typedef std::map<const TupleType, ValueType *, TupleCompare> ContainerType;
ContainerType Pool;
+ uint32_t NextPoolID;
};
// The global constant pool bundles individual pools of each type of
@@ -171,6 +183,25 @@
this, Ty, RelocatableTuple(Offset, Name, SuppressMangling));
}
+ConstantList GlobalContext::getConstantPool(Type Ty) const {
+ switch (Ty) {
+ case IceType_i1:
+ case IceType_i8:
+ case IceType_i16:
+ case IceType_i32:
+ case IceType_i64:
+ return ConstPool->Integers.getConstantPool();
+ case IceType_f32:
+ return ConstPool->Floats.getConstantPool();
+ case IceType_f64:
+ return ConstPool->Doubles.getConstantPool();
+ case IceType_void:
+ case IceType_NUM:
+ break;
+ }
+ llvm_unreachable("Unknown type");
+}
+
void Timer::printElapsedUs(GlobalContext *Ctx, const IceString &Tag) const {
if (Ctx->isVerbose(IceV_Timing)) {
// Prefixing with '#' allows timing strings to be included
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 1ad5f07..beb5bc0 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -77,6 +77,9 @@
// Returns a symbolic constant.
Constant *getConstantSym(Type Ty, int64_t Offset, const IceString &Name = "",
bool SuppressMangling = false);
+ // getConstantPool() returns a copy of the constant pool for
+ // constants of a given type.
+ ConstantList getConstantPool(Type Ty) const;
// Allocate data of type T using the global allocator.
template <typename T> T *allocate() { return Allocator.Allocate<T>(); }
diff --git a/src/IceOperand.h b/src/IceOperand.h
index c75be78..ef4413f 100644
--- a/src/IceOperand.h
+++ b/src/IceOperand.h
@@ -80,6 +80,7 @@
// constants are allocated from a global arena and are pooled.
class Constant : public Operand {
public:
+ uint32_t getPoolEntryID() const { return PoolEntryID; }
virtual void emit(const Cfg *Func) const = 0;
virtual void dump(const Cfg *Func) const = 0;
@@ -89,11 +90,16 @@
}
protected:
- Constant(OperandKind Kind, Type Ty) : Operand(Kind, Ty) {
+ Constant(OperandKind Kind, Type Ty, uint32_t PoolEntryID)
+ : Operand(Kind, Ty), PoolEntryID(PoolEntryID) {
Vars = NULL;
NumVars = 0;
}
virtual ~Constant() {}
+ // PoolEntryID is an integer that uniquely identifies the constant
+ // within its constant pool. It is used for building the constant
+ // pool in the object code and for referencing its entries.
+ const uint32_t PoolEntryID;
private:
Constant(const Constant &) LLVM_DELETED_FUNCTION;
@@ -104,9 +110,10 @@
template <typename T, Operand::OperandKind K>
class ConstantPrimitive : public Constant {
public:
- static ConstantPrimitive *create(GlobalContext *Ctx, Type Ty, T Value) {
+ static ConstantPrimitive *create(GlobalContext *Ctx, Type Ty, T Value,
+ uint32_t PoolEntryID) {
return new (Ctx->allocate<ConstantPrimitive>())
- ConstantPrimitive(Ty, Value);
+ ConstantPrimitive(Ty, Value, PoolEntryID);
}
T getValue() const { return Value; }
virtual void emit(const Cfg *Func) const {
@@ -123,7 +130,8 @@
}
private:
- ConstantPrimitive(Type Ty, T Value) : Constant(K, Ty), Value(Value) {}
+ ConstantPrimitive(Type Ty, T Value, uint32_t PoolEntryID)
+ : Constant(K, Ty, PoolEntryID), Value(Value) {}
ConstantPrimitive(const ConstantPrimitive &) LLVM_DELETED_FUNCTION;
ConstantPrimitive &operator=(const ConstantPrimitive &) LLVM_DELETED_FUNCTION;
virtual ~ConstantPrimitive() {}
@@ -161,9 +169,10 @@
class ConstantRelocatable : public Constant {
public:
static ConstantRelocatable *create(GlobalContext *Ctx, Type Ty,
- const RelocatableTuple &Tuple) {
+ const RelocatableTuple &Tuple,
+ uint32_t PoolEntryID) {
return new (Ctx->allocate<ConstantRelocatable>()) ConstantRelocatable(
- Ty, Tuple.Offset, Tuple.Name, Tuple.SuppressMangling);
+ Ty, Tuple.Offset, Tuple.Name, Tuple.SuppressMangling, PoolEntryID);
}
int64_t getOffset() const { return Offset; }
IceString getName() const { return Name; }
@@ -179,9 +188,9 @@
private:
ConstantRelocatable(Type Ty, int64_t Offset, const IceString &Name,
- bool SuppressMangling)
- : Constant(kConstRelocatable, Ty), Offset(Offset), Name(Name),
- SuppressMangling(SuppressMangling) {}
+ bool SuppressMangling, uint32_t PoolEntryID)
+ : Constant(kConstRelocatable, Ty, PoolEntryID), Offset(Offset),
+ Name(Name), SuppressMangling(SuppressMangling) {}
ConstantRelocatable(const ConstantRelocatable &) LLVM_DELETED_FUNCTION;
ConstantRelocatable &
operator=(const ConstantRelocatable &) LLVM_DELETED_FUNCTION;
diff --git a/src/IceTargetLowering.h b/src/IceTargetLowering.h
index a24c51d..92a36af 100644
--- a/src/IceTargetLowering.h
+++ b/src/IceTargetLowering.h
@@ -149,6 +149,8 @@
virtual void addProlog(CfgNode *Node) = 0;
virtual void addEpilog(CfgNode *Node) = 0;
+ virtual void emitConstants() const = 0;
+
virtual ~TargetLowering() {}
protected:
diff --git a/src/IceTargetLoweringX8632.cpp b/src/IceTargetLoweringX8632.cpp
index 32246c4..8ee5ac9 100644
--- a/src/IceTargetLoweringX8632.cpp
+++ b/src/IceTargetLoweringX8632.cpp
@@ -562,6 +562,71 @@
}
}
+template <typename T> struct PoolTypeConverter {};
+
+template <> struct PoolTypeConverter<float> {
+ typedef float PrimitiveFpType;
+ typedef uint32_t PrimitiveIntType;
+ typedef ConstantFloat IceType;
+ static const Type Ty = IceType_f32;
+ static const char *TypeName;
+ static const char *AsmTag;
+ static const char *PrintfString;
+};
+const char *PoolTypeConverter<float>::TypeName = "float";
+const char *PoolTypeConverter<float>::AsmTag = ".long";
+const char *PoolTypeConverter<float>::PrintfString = "0x%x";
+
+template <> struct PoolTypeConverter<double> {
+ typedef double PrimitiveFpType;
+ typedef uint64_t PrimitiveIntType;
+ typedef ConstantDouble IceType;
+ static const Type Ty = IceType_f64;
+ static const char *TypeName;
+ static const char *AsmTag;
+ static const char *PrintfString;
+};
+const char *PoolTypeConverter<double>::TypeName = "double";
+const char *PoolTypeConverter<double>::AsmTag = ".quad";
+const char *PoolTypeConverter<double>::PrintfString = "0x%llx";
+
+template <typename T> void TargetX8632::emitConstantPool() const {
+ Ostream &Str = Ctx->getStrEmit();
+ Type Ty = T::Ty;
+ SizeT Align = typeAlignInBytes(Ty);
+ ConstantList Pool = Ctx->getConstantPool(Ty);
+
+ Str << "\t.section\t.rodata.cst" << Align << ",\"aM\",@progbits," << Align
+ << "\n";
+ Str << "\t.align\t" << Align << "\n";
+ for (ConstantList::const_iterator I = Pool.begin(), E = Pool.end(); I != E;
+ ++I) {
+ typename T::IceType *Const = llvm::cast<typename T::IceType>(*I);
+ typename T::PrimitiveFpType Value = Const->getValue();
+ // Use memcpy() to copy bits from Value into RawValue in a way
+ // that avoids breaking strict-aliasing rules.
+ typename T::PrimitiveIntType RawValue;
+ memcpy(&RawValue, &Value, sizeof(Value));
+ char buf[30];
+ int CharsPrinted =
+ snprintf(buf, llvm::array_lengthof(buf), T::PrintfString, RawValue);
+ assert(CharsPrinted >= 0 &&
+ (size_t)CharsPrinted < llvm::array_lengthof(buf));
+ (void)CharsPrinted; // avoid warnings if asserts are disabled
+ Str << "L$" << Ty << "$" << Const->getPoolEntryID() << ":\n";
+ Str << "\t" << T::AsmTag << "\t" << buf << "\t# " << T::TypeName << " "
+ << Value << "\n";
+ }
+}
+
+void TargetX8632::emitConstants() const {
+ emitConstantPool<PoolTypeConverter<float> >();
+ emitConstantPool<PoolTypeConverter<double> >();
+
+ // No need to emit constants from the int pool since (for x86) they
+ // are embedded as immediates in the instructions.
+}
+
void TargetX8632::split64(Variable *Var) {
switch (Var->getType()) {
default:
@@ -1878,4 +1943,16 @@
}
}
+template <> void ConstantFloat::emit(const Cfg *Func) const {
+ Ostream &Str = Func->getContext()->getStrEmit();
+ // It would be better to prefix with ".L$" instead of "L$", but
+ // llvm-mc doesn't parse "dword ptr [.L$foo]".
+ Str << "dword ptr [L$" << IceType_f32 << "$" << getPoolEntryID() << "]";
+}
+
+template <> void ConstantDouble::emit(const Cfg *Func) const {
+ Ostream &Str = Func->getContext()->getStrEmit();
+ Str << "qword ptr [L$" << IceType_f64 << "$" << getPoolEntryID() << "]";
+}
+
} // end of namespace Ice
diff --git a/src/IceTargetLoweringX8632.h b/src/IceTargetLoweringX8632.h
index 38184de..e5f8be2 100644
--- a/src/IceTargetLoweringX8632.h
+++ b/src/IceTargetLoweringX8632.h
@@ -47,6 +47,7 @@
virtual void emitVariable(const Variable *Var, const Cfg *Func) const;
virtual void addProlog(CfgNode *Node);
virtual void addEpilog(CfgNode *Node);
+ virtual void emitConstants() const;
SizeT makeNextLabelNumber() { return NextLabelNumber++; }
// Ensure that a 64-bit Variable has been split into 2 32-bit
// Variables, creating them if necessary. This is needed for all
@@ -261,8 +262,12 @@
TargetX8632(const TargetX8632 &) LLVM_DELETED_FUNCTION;
TargetX8632 &operator=(const TargetX8632 &) LLVM_DELETED_FUNCTION;
virtual ~TargetX8632() {}
+ template <typename T> void emitConstantPool() const;
};
+template <> void ConstantFloat::emit(const Cfg *Func) const;
+template <> void ConstantDouble::emit(const Cfg *Func) const;
+
} // end of namespace Ice
#endif // SUBZERO_SRC_ICETARGETLOWERINGX8632_H
diff --git a/src/llvm2ice.cpp b/src/llvm2ice.cpp
index 08f90f8..e29637d 100644
--- a/src/llvm2ice.cpp
+++ b/src/llvm2ice.cpp
@@ -19,6 +19,7 @@
#include "IceGlobalContext.h"
#include "IceInst.h"
#include "IceOperand.h"
+#include "IceTargetLowering.h"
#include "IceTypes.h"
#include "llvm/IR/Constant.h"
@@ -63,6 +64,7 @@
SubzeroPointerType = Ice::IceType_i32;
}
+ // Caller is expected to delete the returned Ice::Cfg object.
Ice::Cfg *convertFunction(const Function *F) {
VarMap.clear();
NodeMap.clear();
@@ -618,14 +620,11 @@
static cl::opt<bool> SubzeroTimingEnabled(
"timing", cl::desc("Enable breakdown timing of Subzero translation"));
-static cl::opt<NaClFileFormat>
-InputFileFormat(
- "bitcode-format",
- cl::desc("Define format of input file:"),
- cl::values(
- clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"),
- clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"),
- clEnumValEnd),
+static cl::opt<NaClFileFormat> InputFileFormat(
+ "bitcode-format", cl::desc("Define format of input file:"),
+ cl::values(clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"),
+ clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"),
+ clEnumValEnd),
cl::init(LLVMFormat));
int main(int argc, char **argv) {
@@ -670,6 +669,15 @@
raw_os_ostream *Ls = new raw_os_ostream(LogFilename == "-" ? std::cout : Lfs);
Ls->SetUnbuffered();
+ // Ideally, Func would be declared inside the loop and its object
+ // would be automatically deleted at the end of the loop iteration.
+ // However, emitting the constant pool requires a valid Cfg object,
+ // so we need to defer deleting the last non-empty Cfg object until
+ // outside the loop and after emitting the constant pool. TODO:
+ // Since all constants are globally pooled in the Ice::GlobalContext
+ // object, change all Ice::Constant related functions to use
+ // GlobalContext instead of Cfg, and then clean up this loop.
+ OwningPtr<Ice::Cfg> Func;
Ice::GlobalContext Ctx(Ls, Os, VMask, TargetArch, OptLevel, TestPrefix);
for (Module::const_iterator I = Mod->begin(), E = Mod->end(); I != E; ++I) {
@@ -678,7 +686,7 @@
LLVM2ICEConverter FunctionConverter(&Ctx);
Ice::Timer TConvert;
- Ice::Cfg *Func = FunctionConverter.convertFunction(I);
+ Func.reset(FunctionConverter.convertFunction(I));
if (DisableInternal)
Func->setInternal(false);
@@ -713,5 +721,8 @@
}
}
+ if (!DisableTranslation && Func)
+ Func->getTarget()->emitConstants();
+
return ExitStatus;
}
diff --git a/tests_lit/llvm2ice_tests/fpconst.pnacl.ll b/tests_lit/llvm2ice_tests/fpconst.pnacl.ll
index 6ca41e4..9bbfe3b 100644
--- a/tests_lit/llvm2ice_tests/fpconst.pnacl.ll
+++ b/tests_lit/llvm2ice_tests/fpconst.pnacl.ll
@@ -6,6 +6,7 @@
; number in a reasonable number of digits". See
; http://llvm.org/docs/LangRef.html#simple-constants .
+; RUN: %llvm2ice -Om1 --verbose none %s | FileCheck %s
; RUN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %llvm2iceinsts %s | %szdiff %s | FileCheck --check-prefix=DUMP %s
; RUN: %llvm2iceinsts --pnacl %s | %szdiff %s \
@@ -534,5 +535,16 @@
ret double %retval.0
}
+; The FP constant pool entries for each type are dumped in some
+; implementation-dependent order. So for the purposes of lit, we just
+; pick one value for each type, and make sure it appears exactly once.
+
+; Check for float 0.5
+; CHECK: .long 0x3f000000
+; CHECK-NOT: .long 0x3f000000
+; Check for double 0.5
+; CHECK: .quad 0x3fe0000000000000
+; CHECK-NOT: .quad 0x3fe0000000000000
+
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ