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